diff --git a/src/main/java/com/example/solidconnection/admin/service/AdminGpaScoreService.java b/src/main/java/com/example/solidconnection/admin/service/AdminGpaScoreService.java index 064e07f89..90fea25e5 100644 --- a/src/main/java/com/example/solidconnection/admin/service/AdminGpaScoreService.java +++ b/src/main/java/com/example/solidconnection/admin/service/AdminGpaScoreService.java @@ -10,14 +10,15 @@ import com.example.solidconnection.application.domain.Gpa; import com.example.solidconnection.common.VerifyStatus; import com.example.solidconnection.common.discord.DiscordNotificationType; -import com.example.solidconnection.common.discord.DiscordNotifier; import com.example.solidconnection.common.discord.DiscordReviewMarker; +import com.example.solidconnection.common.discord.event.DiscordReviewCompletedEvent; import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.score.domain.GpaScore; import com.example.solidconnection.score.repository.GpaScoreRepository; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.repository.SiteUserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -29,7 +30,7 @@ public class AdminGpaScoreService { private final GpaScoreRepository gpaScoreRepository; private final SiteUserRepository siteUserRepository; - private final DiscordNotifier discordNotifier; + private final ApplicationEventPublisher eventPublisher; @Transactional(readOnly = true) public Page searchGpaScores(ScoreSearchCondition scoreSearchCondition, Pageable pageable) { @@ -64,11 +65,11 @@ private void publishReviewResult(GpaScore gpaScore, VerifyStatus verifyStatus) { } SiteUser siteUser = siteUserRepository.findById(gpaScore.getSiteUserId()) .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); - discordNotifier.markReviewResult( + eventPublisher.publishEvent(new DiscordReviewCompletedEvent( DiscordNotificationType.GPA_SCORE, gpaScore.getId(), siteUser.getNickname(), marker - ); + )); } } diff --git a/src/main/java/com/example/solidconnection/admin/service/AdminLanguageTestScoreService.java b/src/main/java/com/example/solidconnection/admin/service/AdminLanguageTestScoreService.java index d52966f30..09ac4ca34 100644 --- a/src/main/java/com/example/solidconnection/admin/service/AdminLanguageTestScoreService.java +++ b/src/main/java/com/example/solidconnection/admin/service/AdminLanguageTestScoreService.java @@ -10,14 +10,15 @@ import com.example.solidconnection.application.domain.LanguageTest; import com.example.solidconnection.common.VerifyStatus; import com.example.solidconnection.common.discord.DiscordNotificationType; -import com.example.solidconnection.common.discord.DiscordNotifier; import com.example.solidconnection.common.discord.DiscordReviewMarker; +import com.example.solidconnection.common.discord.event.DiscordReviewCompletedEvent; import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.score.domain.LanguageTestScore; import com.example.solidconnection.score.repository.LanguageTestScoreRepository; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.repository.SiteUserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -29,7 +30,7 @@ public class AdminLanguageTestScoreService { private final LanguageTestScoreRepository languageTestScoreRepository; private final SiteUserRepository siteUserRepository; - private final DiscordNotifier discordNotifier; + private final ApplicationEventPublisher eventPublisher; @Transactional(readOnly = true) public Page searchLanguageTestScores(ScoreSearchCondition scoreSearchCondition, Pageable pageable) { @@ -64,11 +65,11 @@ private void publishReviewResult(LanguageTestScore languageTestScore, VerifyStat } SiteUser siteUser = siteUserRepository.findById(languageTestScore.getSiteUserId()) .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); - discordNotifier.markReviewResult( + eventPublisher.publishEvent(new DiscordReviewCompletedEvent( DiscordNotificationType.LANGUAGE_TEST_SCORE, languageTestScore.getId(), siteUser.getNickname(), marker - ); + )); } } diff --git a/src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java b/src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java index a2cbed69a..d0741e404 100644 --- a/src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java +++ b/src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java @@ -10,8 +10,8 @@ import com.example.solidconnection.admin.dto.MentorApplicationSearchCondition; import com.example.solidconnection.admin.dto.MentorApplicationSearchResponse; import com.example.solidconnection.common.discord.DiscordNotificationType; -import com.example.solidconnection.common.discord.DiscordNotifier; import com.example.solidconnection.common.discord.DiscordReviewMarker; +import com.example.solidconnection.common.discord.event.DiscordReviewCompletedEvent; import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.mentor.domain.Mentor; import com.example.solidconnection.mentor.domain.MentorApplication; @@ -25,6 +25,7 @@ import java.util.List; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -38,7 +39,7 @@ public class AdminMentorApplicationService { private final HostUniversityRepository hostUniversityRepository; private final SiteUserRepository siteUserRepository; private final MentorRepository mentorRepository; - private final DiscordNotifier discordNotifier; + private final ApplicationEventPublisher eventPublisher; @Transactional(readOnly = true) public Page searchMentorApplications( @@ -91,12 +92,12 @@ public void rejectMentorApplication( } private void publishReviewResult(long mentorApplicationId, String applicantInfo, String marker) { - discordNotifier.markReviewResult( + eventPublisher.publishEvent(new DiscordReviewCompletedEvent( DiscordNotificationType.MENTOR_APPLICATION, mentorApplicationId, applicantInfo, marker - ); + )); } @Transactional(readOnly = true) diff --git a/src/main/java/com/example/solidconnection/common/discord/DiscordNotificationEventListener.java b/src/main/java/com/example/solidconnection/common/discord/DiscordNotificationEventListener.java new file mode 100644 index 000000000..4642ab326 --- /dev/null +++ b/src/main/java/com/example/solidconnection/common/discord/DiscordNotificationEventListener.java @@ -0,0 +1,48 @@ +package com.example.solidconnection.common.discord; + +import com.example.solidconnection.common.discord.event.DiscordReportCreatedEvent; +import com.example.solidconnection.common.discord.event.DiscordReviewCompletedEvent; +import com.example.solidconnection.common.discord.event.DiscordReviewRequestedEvent; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +@EnableAsync +@RequiredArgsConstructor +@Slf4j +public class DiscordNotificationEventListener { + + private final DiscordNotifier discordNotifier; + + @Async("asyncExecutor") + @TransactionalEventListener + public void handleReviewRequested(DiscordReviewRequestedEvent event) { + try { + discordNotifier.notify(event.type(), event.reviewId(), event.applicantInfo()); + } catch (Exception e) { + log.error("검수 요청 알림을 전송하지 못했습니다. reviewType={}, reviewId={}, exception={}", + event.type(), event.reviewId(), e.getClass().getSimpleName()); + } + } + + @Async("asyncExecutor") + @TransactionalEventListener + public void handleReviewCompleted(DiscordReviewCompletedEvent event) { + try { + discordNotifier.markReviewResult(event.type(), event.reviewId(), event.applicantInfo(), event.marker()); + } catch (Exception e) { + log.error("검수 결과를 표시하지 못했습니다. reviewType={}, reviewId={}, exception={}", + event.type(), event.reviewId(), e.getClass().getSimpleName()); + } + } + + @Async("asyncExecutor") + @TransactionalEventListener + public void handleReportCreated(DiscordReportCreatedEvent event) { + discordNotifier.notify(DiscordNotificationType.REPORT, "신고자 ID: " + event.reporterId()); + } +} diff --git a/src/main/java/com/example/solidconnection/common/discord/DiscordNotifier.java b/src/main/java/com/example/solidconnection/common/discord/DiscordNotifier.java index 6c15e24a4..6d5bf8b91 100644 --- a/src/main/java/com/example/solidconnection/common/discord/DiscordNotifier.java +++ b/src/main/java/com/example/solidconnection/common/discord/DiscordNotifier.java @@ -4,13 +4,10 @@ import com.example.solidconnection.common.discord.service.DiscordNotificationService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; @Component -@EnableAsync @Slf4j public class DiscordNotifier { @@ -33,7 +30,6 @@ public DiscordNotifier( this.environment = environment; } - @Async public void notify(DiscordNotificationType type, String applicantInfo) { if (isNotifyDisabled()) { return; diff --git a/src/main/java/com/example/solidconnection/common/discord/event/DiscordReportCreatedEvent.java b/src/main/java/com/example/solidconnection/common/discord/event/DiscordReportCreatedEvent.java new file mode 100644 index 000000000..5abd4e81b --- /dev/null +++ b/src/main/java/com/example/solidconnection/common/discord/event/DiscordReportCreatedEvent.java @@ -0,0 +1,7 @@ +package com.example.solidconnection.common.discord.event; + +public record DiscordReportCreatedEvent( + long reporterId +) { + +} diff --git a/src/main/java/com/example/solidconnection/common/discord/event/DiscordReviewCompletedEvent.java b/src/main/java/com/example/solidconnection/common/discord/event/DiscordReviewCompletedEvent.java new file mode 100644 index 000000000..35045e211 --- /dev/null +++ b/src/main/java/com/example/solidconnection/common/discord/event/DiscordReviewCompletedEvent.java @@ -0,0 +1,12 @@ +package com.example.solidconnection.common.discord.event; + +import com.example.solidconnection.common.discord.DiscordNotificationType; + +public record DiscordReviewCompletedEvent( + DiscordNotificationType type, + long reviewId, + String applicantInfo, + String marker +) { + +} diff --git a/src/main/java/com/example/solidconnection/common/discord/event/DiscordReviewRequestedEvent.java b/src/main/java/com/example/solidconnection/common/discord/event/DiscordReviewRequestedEvent.java new file mode 100644 index 000000000..4274a0402 --- /dev/null +++ b/src/main/java/com/example/solidconnection/common/discord/event/DiscordReviewRequestedEvent.java @@ -0,0 +1,11 @@ +package com.example.solidconnection.common.discord.event; + +import com.example.solidconnection.common.discord.DiscordNotificationType; + +public record DiscordReviewRequestedEvent( + DiscordNotificationType type, + long reviewId, + String applicantInfo +) { + +} diff --git a/src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java b/src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java index 48679daf1..8cff2cbdc 100644 --- a/src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java +++ b/src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java @@ -6,7 +6,7 @@ import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; import com.example.solidconnection.common.discord.DiscordNotificationType; -import com.example.solidconnection.common.discord.DiscordNotifier; +import com.example.solidconnection.common.discord.event.DiscordReviewRequestedEvent; import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.mentor.domain.MentorApplication; import com.example.solidconnection.mentor.domain.MentorApplicationStatus; @@ -22,6 +22,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -37,7 +38,7 @@ public class MentorApplicationService { private final SiteUserRepository siteUserRepository; private final S3Service s3Service; private final TermRepository termRepository; - private final DiscordNotifier discordNotifier; + private final ApplicationEventPublisher eventPublisher; @Transactional public void submitMentorApplication( @@ -63,11 +64,11 @@ public void submitMentorApplication( mentorApplicationRequest.exchangeStatus() ); MentorApplication savedMentorApplication = mentorApplicationRepository.save(mentorApplication); - discordNotifier.notify( + eventPublisher.publishEvent(new DiscordReviewRequestedEvent( DiscordNotificationType.MENTOR_APPLICATION, savedMentorApplication.getId(), siteUser.getNickname() - ); + )); } private void ensureNoPendingOrApprovedMentorApplication(long siteUserId) { diff --git a/src/main/java/com/example/solidconnection/report/service/ReportService.java b/src/main/java/com/example/solidconnection/report/service/ReportService.java index 06015568e..2c8c7fbbd 100644 --- a/src/main/java/com/example/solidconnection/report/service/ReportService.java +++ b/src/main/java/com/example/solidconnection/report/service/ReportService.java @@ -4,8 +4,7 @@ import com.example.solidconnection.chat.domain.ChatParticipant; import com.example.solidconnection.chat.repository.ChatMessageRepository; import com.example.solidconnection.chat.repository.ChatParticipantRepository; -import com.example.solidconnection.common.discord.DiscordNotificationType; -import com.example.solidconnection.common.discord.DiscordNotifier; +import com.example.solidconnection.common.discord.event.DiscordReportCreatedEvent; import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.common.exception.ErrorCode; import com.example.solidconnection.community.post.domain.Post; @@ -19,6 +18,7 @@ import com.example.solidconnection.siteuser.repository.SiteUserRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @Service @@ -30,7 +30,7 @@ public class ReportService { private final PostRepository postRepository; private final ChatMessageRepository chatMessageRepository; private final ChatParticipantRepository chatParticipantRepository; - private final DiscordNotifier discordNotifier; + private final ApplicationEventPublisher eventPublisher; @Transactional public void createReport(long reporterId, ReportRequest request) { @@ -42,7 +42,7 @@ public void createReport(long reporterId, ReportRequest request) { Report report = new Report(reporterId, reportedId, request.reportType(), request.targetType(), request.targetId()); reportRepository.save(report); - discordNotifier.notify(DiscordNotificationType.REPORT, "신고자 ID: " + reporterId); + eventPublisher.publishEvent(new DiscordReportCreatedEvent(reporterId)); } private void validateReporterAndReportedExists(long reporterId, long reportedId) { diff --git a/src/main/java/com/example/solidconnection/score/service/ScoreService.java b/src/main/java/com/example/solidconnection/score/service/ScoreService.java index d84aeee7f..a2abc2831 100644 --- a/src/main/java/com/example/solidconnection/score/service/ScoreService.java +++ b/src/main/java/com/example/solidconnection/score/service/ScoreService.java @@ -5,7 +5,7 @@ import com.example.solidconnection.application.domain.Gpa; import com.example.solidconnection.application.domain.LanguageTest; import com.example.solidconnection.common.discord.DiscordNotificationType; -import com.example.solidconnection.common.discord.DiscordNotifier; +import com.example.solidconnection.common.discord.event.DiscordReviewRequestedEvent; import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.s3.domain.UploadPath; import com.example.solidconnection.s3.dto.UploadedFileUrlResponse; @@ -26,6 +26,7 @@ import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -39,7 +40,7 @@ public class ScoreService { private final LanguageTestScoreRepository languageTestScoreRepository; private final SiteUserRepository siteUserRepository; private final HomeUniversityQueryService homeUniversityQueryService; - private final DiscordNotifier discordNotifier; + private final ApplicationEventPublisher eventPublisher; @Transactional public Long submitGpaScore(long siteUserId, GpaScoreRequest gpaScoreRequest, MultipartFile file) { @@ -49,7 +50,11 @@ public Long submitGpaScore(long siteUserId, GpaScoreRequest gpaScoreRequest, Mul Gpa gpa = new Gpa(gpaScoreRequest.gpa(), gpaScoreRequest.gpaCriteria(), uploadedFile.fileUrl()); GpaScore newGpaScore = new GpaScore(gpa, siteUser); GpaScore savedNewGpaScore = gpaScoreRepository.save(newGpaScore); - discordNotifier.notify(DiscordNotificationType.GPA_SCORE, savedNewGpaScore.getId(), siteUser.getNickname()); + eventPublisher.publishEvent(new DiscordReviewRequestedEvent( + DiscordNotificationType.GPA_SCORE, + savedNewGpaScore.getId(), + siteUser.getNickname() + )); return savedNewGpaScore.getId(); } @@ -62,7 +67,11 @@ public Long submitLanguageTestScore(long siteUserId, LanguageTestScoreRequest la languageTestScoreRequest.languageTestScore(), uploadedFile.fileUrl()); LanguageTestScore newScore = new LanguageTestScore(languageTest, siteUser); LanguageTestScore savedNewScore = languageTestScoreRepository.save(newScore); - discordNotifier.notify(DiscordNotificationType.LANGUAGE_TEST_SCORE, savedNewScore.getId(), siteUser.getNickname()); + eventPublisher.publishEvent(new DiscordReviewRequestedEvent( + DiscordNotificationType.LANGUAGE_TEST_SCORE, + savedNewScore.getId(), + siteUser.getNickname() + )); return savedNewScore.getId(); } diff --git a/src/test/java/com/example/solidconnection/admin/service/AdminGpaScoreServiceTest.java b/src/test/java/com/example/solidconnection/admin/service/AdminGpaScoreServiceTest.java index e8f25dbba..8e760e423 100644 --- a/src/test/java/com/example/solidconnection/admin/service/AdminGpaScoreServiceTest.java +++ b/src/test/java/com/example/solidconnection/admin/service/AdminGpaScoreServiceTest.java @@ -6,6 +6,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.willThrow; import com.example.solidconnection.admin.dto.GpaScoreResponse; import com.example.solidconnection.admin.dto.GpaScoreSearchResponse; @@ -15,6 +19,7 @@ import com.example.solidconnection.common.exception.CustomException; import com.example.solidconnection.score.domain.GpaScore; import com.example.solidconnection.score.fixture.GpaScoreFixture; +import com.example.solidconnection.score.repository.GpaScoreRepository; import com.example.solidconnection.siteuser.domain.SiteUser; import com.example.solidconnection.siteuser.fixture.SiteUserFixture; import com.example.solidconnection.support.TestContainerSpringBootTest; @@ -28,6 +33,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.web.client.RestClientException; @TestContainerSpringBootTest @DisplayName("학점 검증 관리자 서비스 테스트") @@ -36,6 +42,9 @@ class AdminGpaScoreServiceTest { @Autowired private AdminGpaScoreService adminGpaScoreService; + @Autowired + private GpaScoreRepository gpaScoreRepository; + /* * - 테스트 설정의 discord.webhook-url 이 비어 있지 않아 실제 요청이 나갈 수 있으므로 목으로 대체한다. * */ @@ -185,5 +194,25 @@ class GPA_점수_검증_및_수정 { .isInstanceOf(CustomException.class) .hasMessage(GPA_SCORE_NOT_FOUND.getMessage()); } + + @Test + void 디스코드_검수_결과_표시에_실패해도_검증상태를_수정한다() { + // given + GpaScoreUpdateRequest request = new GpaScoreUpdateRequest( + 3.8, + 4.3, + VerifyStatus.APPROVED, + null + ); + willThrow(new RestClientException("discord unavailable")) + .given(discordNotifier).markReviewResult(any(), anyLong(), anyString(), anyString()); + + // when + adminGpaScoreService.updateGpaScore(gpaScore1.getId(), request); + + // then + GpaScore updatedGpaScore = gpaScoreRepository.findById(gpaScore1.getId()).orElseThrow(); + assertThat(updatedGpaScore.getVerifyStatus()).isEqualTo(VerifyStatus.APPROVED); + } } } diff --git a/src/test/java/com/example/solidconnection/common/discord/DiscordNotificationEventListenerTest.java b/src/test/java/com/example/solidconnection/common/discord/DiscordNotificationEventListenerTest.java new file mode 100644 index 000000000..ffa8d3db8 --- /dev/null +++ b/src/test/java/com/example/solidconnection/common/discord/DiscordNotificationEventListenerTest.java @@ -0,0 +1,56 @@ +package com.example.solidconnection.common.discord; + +import static org.awaitility.Awaitility.await; +import static org.mockito.BDDMockito.then; + +import com.example.solidconnection.common.discord.event.DiscordReviewRequestedEvent; +import com.example.solidconnection.support.TestContainerSpringBootTest; +import java.time.Duration; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.transaction.support.TransactionTemplate; + +@TestContainerSpringBootTest +@DisplayName("디스코드 알림 이벤트 리스너 테스트") +class DiscordNotificationEventListenerTest { + + private static final DiscordReviewRequestedEvent EVENT = + new DiscordReviewRequestedEvent(DiscordNotificationType.GPA_SCORE, 1L, "홍길동"); + + @Autowired + private ApplicationEventPublisher eventPublisher; + + @Autowired + private TransactionTemplate transactionTemplate; + + @MockitoBean + private DiscordNotifier discordNotifier; + + @Test + void 트랜잭션이_커밋되면_알림을_전송한다() { + // when + transactionTemplate.executeWithoutResult(status -> eventPublisher.publishEvent(EVENT)); + + // then + await().atMost(Duration.ofSeconds(3)) + .untilAsserted(() -> then(discordNotifier).should() + .notify(EVENT.type(), EVENT.reviewId(), EVENT.applicantInfo())); + } + + @Test + void 트랜잭션이_롤백되면_알림을_전송하지_않는다() { + // when + transactionTemplate.executeWithoutResult(status -> { + eventPublisher.publishEvent(EVENT); + status.setRollbackOnly(); + }); + + // then + await().during(Duration.ofMillis(300)) + .atMost(Duration.ofSeconds(1)) + .untilAsserted(() -> then(discordNotifier).shouldHaveNoInteractions()); + } +} diff --git a/src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java b/src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java index 89cc8535e..285c6d195 100644 --- a/src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java +++ b/src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java @@ -2,7 +2,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willThrow; import com.example.solidconnection.common.VerifyStatus; import com.example.solidconnection.common.discord.DiscordNotifier; @@ -33,6 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.web.client.RestClientException; @TestContainerSpringBootTest @DisplayName("점수 서비스 테스트") @@ -158,6 +163,22 @@ void setUp() { assertThat(savedScore.getId()).isEqualTo(scoreId); } + @Test + void 디스코드_알림에_실패해도_GPA_점수를_등록한다() { + // given + GpaScoreRequest request = createGpaScoreRequest(); + MockMultipartFile file = createFile(); + given(s3Service.uploadFile(file, UploadPath.GPA)).willReturn(new UploadedFileUrlResponse("/gpa-report.pdf")); + willThrow(new RestClientException("discord unavailable")) + .given(discordNotifier).notify(any(), anyLong(), anyString()); + + // when + long scoreId = scoreService.submitGpaScore(user.getId(), request, file); + + // then + assertThat(gpaScoreRepository.findById(scoreId)).isPresent(); + } + @Test void 어학_시험_점수를_등록한다() { // given