@@ -832,10 +832,225 @@ mod fuzz {
832832
833833 set_project_co2_rate_direct( & env, & cid, & project_id, u32 :: MAX ) ;
834834
835- let result = std:: panic:: catch_unwind( std:: panic:: AssertUnwindSafe ( || {
836- client. donate_usdc( & usdc_token, & donor, & project_id, & usdc_amount, & MSG_HASH ) ;
837- } ) ) ;
835+ let result = client. try_donate_usdc( & usdc_token, & donor, & project_id, & usdc_amount, & MSG_HASH ) ;
838836 prop_assert!( result. is_err( ) , "donate_usdc should panic on CO2 overflow" ) ;
839837 }
838+ } // END of first proptest!
839+
840+ proptest ! {
841+ #![ proptest_config( ProptestConfig :: with_cases( 100 ) ) ]
842+
843+ #[ test]
844+ fn prop_fuzz_badge_weighted_voting(
845+ amount in 10i128 * STROOP ..=10_000i128 * STROOP ,
846+ ) {
847+ let ( env, admin, client, project_id) = setup_with_admin( ) ;
848+ client. create_proposal( & admin, & project_id, & 720u32 ) ;
849+
850+ let token_admin = Address :: generate( & env) ;
851+ let token = env
852+ . register_stellar_asset_contract_v2( token_admin)
853+ . address( ) ;
854+
855+ let donor = Address :: generate( & env) ;
856+ mint_tokens( & env, & token, & donor, amount) ;
857+ client. donate( & token, & donor, & project_id, & amount, & 42u32 ) ;
858+
859+ let stats = client. get_donor_stats( & donor) ;
860+ let expected_weight = match stats. badge {
861+ BadgeTier :: Seedling => 1u32 ,
862+ BadgeTier :: Tree => 3u32 ,
863+ BadgeTier :: Forest => 10u32 ,
864+ BadgeTier :: EarthGuardian => 25u32 ,
865+ BadgeTier :: None => 0u32 ,
866+ } ;
867+
868+ client. vote_verify_project( & donor, & project_id, & true ) ;
869+
870+ let proposal = client. get_proposal( & project_id) ;
871+ prop_assert_eq!( proposal. votes_for, expected_weight) ;
872+ }
873+ } // END of proptest!
874+
875+ #[ test]
876+ fn test_zero_amount_donation_rejected ( ) {
877+ let ( env, _cid, client, project_id, token) = setup ( ) ;
878+ let donor = Address :: generate ( & env) ;
879+ let result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
880+ client. donate ( & token, & donor, & project_id, & 0i128 , & 42u32 ) ;
881+ } ) ) ;
882+ assert ! ( result. is_err( ) , "donate with amount=0 should panic" ) ;
883+ }
884+
885+ #[ test]
886+ fn test_deactivated_project_cannot_be_paused ( ) {
887+ let ( _env, admin, client, project_id) = setup_with_admin ( ) ;
888+
889+ client. deactivate_project ( & admin, & project_id) ;
890+
891+ // Pausing a deactivated project must panic
892+ let result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
893+ client. pause_project ( & admin, & project_id) ;
894+ } ) ) ;
895+ assert ! (
896+ result. is_err( ) ,
897+ "pause_project should panic when project is deactivated"
898+ ) ;
899+
900+ let project = client. get_project ( & project_id) ;
901+ assert ! ( !project. active) ;
902+ }
903+
904+ #[ test]
905+ fn test_zero_co2_rate_rejected ( ) {
906+ let ( _env, admin, client, project_id) = setup_with_admin ( ) ;
907+ let result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
908+ client. update_project_co2_rate ( & admin, & project_id, & 0u32 ) ;
909+ } ) ) ;
910+ assert ! (
911+ result. is_err( ) ,
912+ "update_project_co2_rate with 0 should panic"
913+ ) ;
914+ }
915+
916+ #[ test]
917+ fn test_excessive_co2_rate_rejected ( ) {
918+ let ( _env, admin, client, project_id) = setup_with_admin ( ) ;
919+ let result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
920+ client. update_project_co2_rate ( & admin, & project_id, & ( MAX_CO2_PER_XLM + 1 ) ) ;
921+ } ) ) ;
922+ assert ! (
923+ result. is_err( ) ,
924+ "update_project_co2_rate > MAX should panic"
925+ ) ;
926+ }
927+
928+ #[ test]
929+ fn test_admin_transfer_happy_path ( ) {
930+ let ( env, admin, client, _project_id) = setup_with_admin ( ) ;
931+ let new_admin = Address :: generate ( & env) ;
932+
933+ client. transfer_admin ( & admin, & new_admin) ;
934+ let pending = client. get_pending_admin ( ) ;
935+ assert_eq ! ( pending, Some ( new_admin. clone( ) ) ) ;
936+
937+ client. accept_admin ( ) ;
938+ let stored_admin = client. get_admin ( ) ;
939+ assert_eq ! ( stored_admin, new_admin) ;
940+ assert_eq ! ( client. get_pending_admin( ) , None ) ;
941+ }
942+
943+ #[ test]
944+ fn test_admin_transfer_cancel ( ) {
945+ let ( env, admin, client, _project_id) = setup_with_admin ( ) ;
946+ let new_admin = Address :: generate ( & env) ;
947+ client. transfer_admin ( & admin, & new_admin) ;
948+ assert ! ( client. get_pending_admin( ) . is_some( ) ) ;
949+
950+ client. cancel_admin_transfer ( & admin) ;
951+ assert ! ( client. get_pending_admin( ) . is_none( ) ) ;
952+ assert_eq ! ( client. get_admin( ) , admin) ;
953+ }
954+
955+ #[ test]
956+ fn test_duplicate_project_id_rejected ( ) {
957+ let ( env, admin, client, project_id) = setup_with_admin ( ) ;
958+ let wallet2 = Address :: generate ( & env) ;
959+ let result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
960+ client. register_project (
961+ & admin,
962+ & project_id,
963+ & SorobanString :: from_str ( & env, "Duplicate" ) ,
964+ & wallet2,
965+ & 50u32 ,
966+ ) ;
967+ } ) ) ;
968+ assert ! (
969+ result. is_err( ) ,
970+ "register_project with duplicate ID should panic"
971+ ) ;
972+ }
973+
974+ #[ test]
975+ fn test_veto_before_resolution ( ) {
976+ let ( _env, admin, client, project_id) = setup_with_admin ( ) ;
977+ client. create_proposal ( & admin, & project_id, & 720u32 ) ;
978+ let proposal_before = client. get_proposal ( & project_id) ;
979+ assert ! ( !proposal_before. resolved) ;
980+
981+ client. veto_proposal ( & admin, & project_id) ;
982+ let proposal_after = client. get_proposal ( & project_id) ;
983+ assert ! ( proposal_after. resolved) ;
984+ }
985+
986+ #[ test]
987+ fn test_proposal_default_duration ( ) {
988+ let ( _env, admin, client, project_id) = setup_with_admin ( ) ;
989+ client. create_proposal ( & admin, & project_id, & 0u32 ) ;
990+ let proposal = client. get_proposal ( & project_id) ;
991+ assert ! ( !proposal. resolved) ;
992+ assert_eq ! ( proposal. votes_for, 0u32 ) ;
993+ assert_eq ! ( proposal. votes_against, 0u32 ) ;
994+ }
995+
996+ #[ test]
997+ fn test_deactivate_all_projects ( ) {
998+ let ( env, admin, client, project_id) = setup_with_admin ( ) ;
999+
1000+ let wallet_b = Address :: generate ( & env) ;
1001+ let project_b = SorobanString :: from_str ( & env, "proj-bulk-b" ) ;
1002+ client. register_project (
1003+ & admin,
1004+ & project_b,
1005+ & SorobanString :: from_str ( & env, "Bulk B" ) ,
1006+ & wallet_b,
1007+ & 75u32 ,
1008+ ) ;
1009+
1010+ assert ! ( client. get_project( & project_id) . active) ;
1011+ assert ! ( client. get_project( & project_b) . active) ;
1012+
1013+ client. deactivate_all_projects ( & admin) ;
1014+
1015+ assert ! ( !client. get_project( & project_id) . active) ;
1016+ assert ! ( !client. get_project( & project_b) . active) ;
1017+ }
1018+
1019+ #[ test]
1020+ fn test_badge_weighted_voting_seedling_and_earth_guardian ( ) {
1021+ let ( env, admin, client, project_id) = setup_with_admin ( ) ;
1022+ client. create_proposal ( & admin, & project_id, & 720u32 ) ;
1023+
1024+ let token_admin = Address :: generate ( & env) ;
1025+ let token = env
1026+ . register_stellar_asset_contract_v2 ( token_admin)
1027+ . address ( ) ;
1028+
1029+ let donor_seedling = Address :: generate ( & env) ;
1030+ let amt_seedling = 10i128 * STROOP ;
1031+ mint_tokens ( & env, & token, & donor_seedling, amt_seedling) ;
1032+ client. donate ( & token, & donor_seedling, & project_id, & amt_seedling, & 42u32 ) ;
1033+
1034+ client. vote_verify_project ( & donor_seedling, & project_id, & true ) ;
1035+
1036+ let donor_earth = Address :: generate ( & env) ;
1037+ let amt_earth = 2000i128 * STROOP ;
1038+ mint_tokens ( & env, & token, & donor_earth, amt_earth) ;
1039+ client. donate ( & token, & donor_earth, & project_id, & amt_earth, & 42u32 ) ;
1040+
1041+ client. vote_verify_project ( & donor_earth, & project_id, & true ) ;
1042+
1043+ let proposal = client. get_proposal ( & project_id) ;
1044+ assert_eq ! ( proposal. votes_for, 26u32 ) ;
1045+ }
1046+
1047+ #[ test]
1048+ fn test_badge_weighted_voting_none_tier_panics ( ) {
1049+ let ( env, admin, client, project_id) = setup_with_admin ( ) ;
1050+ client. create_proposal ( & admin, & project_id, & 720u32 ) ;
1051+
1052+ let voter = Address :: generate ( & env) ;
1053+ let result = client. try_vote_verify_project ( & voter, & project_id, & true ) ;
1054+ assert ! ( result. is_err( ) , "None tier voter should panic" ) ;
8401055 }
8411056}
0 commit comments