@@ -608,6 +608,185 @@ void usesLatestFormEmailSendHistoryPerStatus() {
608608 }
609609
610610
611+ @ DisplayName ("재전송 후 overview는 가장 최신 배치만이 아닌 지원자별 누적 현황을 반영한다" )
612+ @ Test
613+ void overviewReflectsAccumulatedStatusAcrossAllBatchesAfterResend () {
614+ // given
615+ User savedUser = userRepository .save (UserFixture .createClubUser ());
616+ Club savedClub = clubRepository .save (ClubFixture .createClub (savedUser ));
617+ Form savedForm = formRepository .save (FormFixture .createForm (savedClub ));
618+
619+ FormEmailSendHistory initialBatch = formEmailSendHistoryRepository .save (
620+ FormEmailSendHistoryFixture .createFinalPass (savedForm ));
621+
622+ // 초기 발송: 3명 성공, 2명 실패
623+ FormApplication successApplication1 = formApplicationRepository .save (
624+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FINAL_PASS ));
625+ FormApplication successApplication2 = formApplicationRepository .save (
626+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FINAL_PASS ));
627+ FormApplication successApplication3 = formApplicationRepository .save (
628+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FINAL_PASS ));
629+ FormApplication failApplication1 = formApplicationRepository .save (
630+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FINAL_PASS ));
631+ FormApplication failApplication2 = formApplicationRepository .save (
632+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FINAL_PASS ));
633+
634+ emailSendHistoryRepository .save (EmailSendHistoryFixture .deliverySuccess (successApplication1 , initialBatch ));
635+ emailSendHistoryRepository .save (EmailSendHistoryFixture .deliverySuccess (successApplication2 , initialBatch ));
636+ emailSendHistoryRepository .save (EmailSendHistoryFixture .deliverySuccess (successApplication3 , initialBatch ));
637+ emailSendHistoryRepository .save (EmailSendHistoryFixture .permanentFailureWithFormEmailSendHistory (failApplication1 , initialBatch ));
638+ emailSendHistoryRepository .save (EmailSendHistoryFixture .permanentFailureWithFormEmailSendHistory (failApplication2 , initialBatch ));
639+
640+ // 재전송: 실패한 2명 중 1명 성공, 1명 여전히 실패
641+ FormEmailSendHistory resendBatch = formEmailSendHistoryRepository .save (
642+ FormEmailSendHistoryFixture .createFinalPass (savedForm ));
643+
644+ emailSendHistoryRepository .save (EmailSendHistoryFixture .deliverySuccess (failApplication1 , resendBatch ));
645+ emailSendHistoryRepository .save (EmailSendHistoryFixture .permanentFailureWithFormEmailSendHistory (failApplication2 , resendBatch ));
646+
647+ // when
648+ EmailSendStatusOverviewQuery result =
649+ facadeCentralFormService .getEmailSendStatusOverviewByFormId (savedForm .getId ());
650+
651+ // then: 재전송 배치(2건)가 아닌 전체 5명의 누적 현황으로 집계돼야 한다
652+ EmailSendStatusOverviewInfoQuery finalPassOverview =
653+ result .emailSendStatusOverviewInfoQueries ().stream ()
654+ .filter (info -> info .formApplicationStatus () == FormApplicationStatus .FINAL_PASS )
655+ .findFirst ()
656+ .orElseThrow ();
657+
658+ assertThat (finalPassOverview .successCount ()).isEqualTo (4 ); // 기존 3명 + 재전송 성공 1명
659+ assertThat (finalPassOverview .failCount ()).isEqualTo (1 ); // 재전송 후에도 실패한 1명
660+ }
661+
662+ @ DisplayName ("재전송을 여러 번 반복해도 지원자별 가장 최신 상태로 집계한다" )
663+ @ Test
664+ void overviewReflectsLatestStatusAfterMultipleResends () {
665+ // given
666+ User savedUser = userRepository .save (UserFixture .createClubUser ());
667+ Club savedClub = clubRepository .save (ClubFixture .createClub (savedUser ));
668+ Form savedForm = formRepository .save (FormFixture .createForm (savedClub ));
669+
670+ FormEmailSendHistory batch1 = formEmailSendHistoryRepository .save (
671+ FormEmailSendHistoryFixture .createFirstPass (savedForm ));
672+
673+ FormApplication application = formApplicationRepository .save (
674+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FIRST_PASS ));
675+
676+ // 1차: 실패
677+ emailSendHistoryRepository .save (
678+ EmailSendHistoryFixture .permanentFailureWithFormEmailSendHistory (application , batch1 ));
679+
680+ // 2차 재전송: 또 실패
681+ FormEmailSendHistory batch2 = formEmailSendHistoryRepository .save (
682+ FormEmailSendHistoryFixture .createFirstPass (savedForm ));
683+ emailSendHistoryRepository .save (
684+ EmailSendHistoryFixture .permanentFailureWithFormEmailSendHistory (application , batch2 ));
685+
686+ // 3차 재전송: 성공
687+ FormEmailSendHistory batch3 = formEmailSendHistoryRepository .save (
688+ FormEmailSendHistoryFixture .createFirstPass (savedForm ));
689+ emailSendHistoryRepository .save (
690+ EmailSendHistoryFixture .deliverySuccess (application , batch3 ));
691+
692+ // when
693+ EmailSendStatusOverviewQuery result =
694+ facadeCentralFormService .getEmailSendStatusOverviewByFormId (savedForm .getId ());
695+
696+ // then: 3번째 배치(최종 성공)가 지원자의 최신 상태
697+ EmailSendStatusOverviewInfoQuery firstPassOverview =
698+ result .emailSendStatusOverviewInfoQueries ().stream ()
699+ .filter (info -> info .formApplicationStatus () == FormApplicationStatus .FIRST_PASS )
700+ .findFirst ()
701+ .orElseThrow ();
702+
703+ assertThat (firstPassOverview .successCount ()).isEqualTo (1 );
704+ assertThat (firstPassOverview .failCount ()).isZero ();
705+ }
706+
707+ @ DisplayName ("재전송 없이 아직 한 번도 이메일을 보내지 않은 status는 빈 현황을 반환한다" )
708+ @ Test
709+ void overviewReturnsEmptyForStatusWithNoEmailHistory () {
710+ // given
711+ User savedUser = userRepository .save (UserFixture .createClubUser ());
712+ Club savedClub = clubRepository .save (ClubFixture .createClub (savedUser ));
713+ Form savedForm = formRepository .save (FormFixture .createForm (savedClub ));
714+
715+ // FIRST_PASS만 이메일 전송, FINAL_PASS는 전송 없음
716+ FormEmailSendHistory firstPassBatch = formEmailSendHistoryRepository .save (
717+ FormEmailSendHistoryFixture .createFirstPass (savedForm ));
718+ FormApplication firstPassApplication = formApplicationRepository .save (
719+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FIRST_PASS ));
720+ emailSendHistoryRepository .save (
721+ EmailSendHistoryFixture .deliverySuccess (firstPassApplication , firstPassBatch ));
722+
723+ // when
724+ EmailSendStatusOverviewQuery result =
725+ facadeCentralFormService .getEmailSendStatusOverviewByFormId (savedForm .getId ());
726+
727+ // then
728+ EmailSendStatusOverviewInfoQuery finalPassOverview =
729+ result .emailSendStatusOverviewInfoQueries ().stream ()
730+ .filter (info -> info .formApplicationStatus () == FormApplicationStatus .FINAL_PASS )
731+ .findFirst ()
732+ .orElseThrow ();
733+
734+ assertThat (finalPassOverview .successCount ()).isZero ();
735+ assertThat (finalPassOverview .failCount ()).isZero ();
736+ assertThat (finalPassOverview .lastSentAt ()).isNull ();
737+ }
738+
739+ @ DisplayName ("FIRST_PASS 재전송이 FINAL_PASS 집계에 영향을 주지 않는다" )
740+ @ Test
741+ void overviewAggregatesEachStatusIndependently () {
742+ // given
743+ User savedUser = userRepository .save (UserFixture .createClubUser ());
744+ Club savedClub = clubRepository .save (ClubFixture .createClub (savedUser ));
745+ Form savedForm = formRepository .save (FormFixture .createForm (savedClub ));
746+
747+ FormEmailSendHistory firstPassBatch = formEmailSendHistoryRepository .save (
748+ FormEmailSendHistoryFixture .createFirstPass (savedForm ));
749+ FormEmailSendHistory finalPassBatch = formEmailSendHistoryRepository .save (
750+ FormEmailSendHistoryFixture .createFinalPass (savedForm ));
751+
752+ FormApplication firstPassApplication = formApplicationRepository .save (
753+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FIRST_PASS ));
754+ FormApplication finalPassApplication = formApplicationRepository .save (
755+ FormApplicationFixture .create (savedForm , FormApplicationStatus .FINAL_PASS ));
756+
757+ emailSendHistoryRepository .save (
758+ EmailSendHistoryFixture .permanentFailureWithFormEmailSendHistory (firstPassApplication , firstPassBatch ));
759+ emailSendHistoryRepository .save (
760+ EmailSendHistoryFixture .deliverySuccess (finalPassApplication , finalPassBatch ));
761+
762+ // FIRST_PASS 재전송 추가
763+ FormEmailSendHistory firstPassResendBatch = formEmailSendHistoryRepository .save (
764+ FormEmailSendHistoryFixture .createFirstPass (savedForm ));
765+ emailSendHistoryRepository .save (
766+ EmailSendHistoryFixture .deliverySuccess (firstPassApplication , firstPassResendBatch ));
767+
768+ // when
769+ EmailSendStatusOverviewQuery result =
770+ facadeCentralFormService .getEmailSendStatusOverviewByFormId (savedForm .getId ());
771+
772+ // then
773+ EmailSendStatusOverviewInfoQuery firstPassOverview =
774+ result .emailSendStatusOverviewInfoQueries ().stream ()
775+ .filter (info -> info .formApplicationStatus () == FormApplicationStatus .FIRST_PASS )
776+ .findFirst ()
777+ .orElseThrow ();
778+ EmailSendStatusOverviewInfoQuery finalPassOverview =
779+ result .emailSendStatusOverviewInfoQueries ().stream ()
780+ .filter (info -> info .formApplicationStatus () == FormApplicationStatus .FINAL_PASS )
781+ .findFirst ()
782+ .orElseThrow ();
783+
784+ assertThat (firstPassOverview .successCount ()).isEqualTo (1 );
785+ assertThat (firstPassOverview .failCount ()).isZero ();
786+ assertThat (finalPassOverview .successCount ()).isEqualTo (1 );
787+ assertThat (finalPassOverview .failCount ()).isZero ();
788+ }
789+
611790 @ DisplayName ("같은 지원자에게 여러 번 이메일을 보낸 경우 지원자별 최신 전송 결과만 집계한다" )
612791 @ Test
613792 void countsLatestEmailSendHistoryPerFormApplication () {
0 commit comments