@@ -693,3 +693,150 @@ fn test_vote_threshold_not_met_while_active() {
693693 CampaignStatus :: Submitted
694694 ) ;
695695}
696+
697+ #[ test]
698+ fn test_overdue_flag_and_escalate ( ) {
699+ let t = setup ( ) ;
700+ let sac = StellarAssetClient :: new ( & t. env , & t. token_addr ) ;
701+
702+ let owner = Address :: generate ( & t. env ) ;
703+ let donor = Address :: generate ( & t. env ) ;
704+ sac. mint ( & donor, & 10_000 ) ;
705+
706+ let deadline = t. env . ledger ( ) . timestamp ( ) + 5000 ;
707+
708+ let cid = t. client . create_campaign (
709+ & owner,
710+ & String :: from_str ( & t. env , "Overdue escalation" ) ,
711+ & 1000i128 ,
712+ & t. token_addr ,
713+ & deadline,
714+ & make_milestones ( & t. env ) ,
715+ & 100i128 ,
716+ & false ,
717+ ) ;
718+
719+ advance_to_campaigning ( & t, cid) ;
720+ t. client . pledge ( & donor, & cid, & 1100 ) ;
721+
722+ // Advance 30+ days past deadline → flag overdue
723+ t. env . ledger ( ) . with_mut ( |l| {
724+ l. timestamp = deadline + 30 * 86_400 + 1 ;
725+ } ) ;
726+
727+ t. client . flag_overdue_milestone ( & cid, & 0 ) ;
728+
729+ // Milestone should have flagged_at set
730+ let ms = t. client . get_milestone ( & cid, & 0 ) ;
731+ assert_eq ! ( ms. status, CrowdfundMilestoneStatus :: Pending ) ;
732+ assert ! ( ms. flagged_at > 0 ) ;
733+
734+ // Escalate too early (before 14-day grace) → should fail
735+ let result = t. client . try_escalate_overdue_milestone ( & cid, & 0 ) ;
736+ assert ! ( result. is_err( ) ) ;
737+
738+ // Advance 14+ days past flagging
739+ t. env . ledger ( ) . with_mut ( |l| {
740+ l. timestamp += 14 * 86_400 + 1 ;
741+ } ) ;
742+
743+ // Escalate now → should succeed
744+ t. client . escalate_overdue_milestone ( & cid, & 0 ) ;
745+
746+ let ms = t. client . get_milestone ( & cid, & 0 ) ;
747+ assert_eq ! ( ms. status, CrowdfundMilestoneStatus :: Rejected ) ;
748+
749+ let campaign = t. client . get_campaign ( & cid) ;
750+ assert_eq ! ( campaign. status, CampaignStatus :: Cancelled ) ;
751+
752+ // Backers can get refunds
753+ let balance_before = t. token . balance ( & donor) ;
754+ t. client . process_refund_batch ( & cid) ;
755+ assert ! ( t. token. balance( & donor) > balance_before) ;
756+ }
757+
758+ #[ test]
759+ fn test_overdue_escalate_not_flagged_fails ( ) {
760+ let t = setup ( ) ;
761+ let sac = StellarAssetClient :: new ( & t. env , & t. token_addr ) ;
762+
763+ let owner = Address :: generate ( & t. env ) ;
764+ let donor = Address :: generate ( & t. env ) ;
765+ sac. mint ( & donor, & 10_000 ) ;
766+
767+ let deadline = t. env . ledger ( ) . timestamp ( ) + 5000 ;
768+
769+ let cid = t. client . create_campaign (
770+ & owner,
771+ & String :: from_str ( & t. env , "Not flagged" ) ,
772+ & 1000i128 ,
773+ & t. token_addr ,
774+ & deadline,
775+ & make_milestones ( & t. env ) ,
776+ & 100i128 ,
777+ & false ,
778+ ) ;
779+
780+ advance_to_campaigning ( & t, cid) ;
781+ t. client . pledge ( & donor, & cid, & 1100 ) ;
782+
783+ // Try to escalate without flagging first → should fail
784+ t. env . ledger ( ) . with_mut ( |l| {
785+ l. timestamp = deadline + 60 * 86_400 ;
786+ } ) ;
787+
788+ let result = t. client . try_escalate_overdue_milestone ( & cid, & 0 ) ;
789+ assert ! ( result. is_err( ) ) ;
790+ }
791+
792+ #[ test]
793+ fn test_overdue_creator_submits_during_grace_period ( ) {
794+ let t = setup ( ) ;
795+ let sac = StellarAssetClient :: new ( & t. env , & t. token_addr ) ;
796+
797+ let owner = Address :: generate ( & t. env ) ;
798+ let donor = Address :: generate ( & t. env ) ;
799+ sac. mint ( & donor, & 10_000 ) ;
800+
801+ let deadline = t. env . ledger ( ) . timestamp ( ) + 5000 ;
802+
803+ let cid = t. client . create_campaign (
804+ & owner,
805+ & String :: from_str ( & t. env , "Grace period save" ) ,
806+ & 1000i128 ,
807+ & t. token_addr ,
808+ & deadline,
809+ & make_milestones ( & t. env ) ,
810+ & 100i128 ,
811+ & false ,
812+ ) ;
813+
814+ advance_to_campaigning ( & t, cid) ;
815+ t. client . pledge ( & donor, & cid, & 1100 ) ;
816+
817+ // Flag overdue
818+ t. env . ledger ( ) . with_mut ( |l| {
819+ l. timestamp = deadline + 30 * 86_400 + 1 ;
820+ } ) ;
821+ t. client . flag_overdue_milestone ( & cid, & 0 ) ;
822+
823+ // Creator submits during grace period
824+ t. client . submit_milestone ( & cid, & 0 ) ;
825+ let ms = t. client . get_milestone ( & cid, & 0 ) ;
826+ assert_eq ! ( ms. status, CrowdfundMilestoneStatus :: Submitted ) ;
827+
828+ // Advance past grace period
829+ t. env . ledger ( ) . with_mut ( |l| {
830+ l. timestamp += 14 * 86_400 + 1 ;
831+ } ) ;
832+
833+ // Escalate should fail — milestone is no longer Pending
834+ let result = t. client . try_escalate_overdue_milestone ( & cid, & 0 ) ;
835+ assert ! ( result. is_err( ) ) ;
836+
837+ // Campaign still active
838+ assert_eq ! (
839+ t. client. get_campaign( & cid) . status,
840+ CampaignStatus :: Executing
841+ ) ;
842+ }
0 commit comments