diff --git a/contracts/milestone-escrow/src/admin_override_cancel_tests.rs b/contracts/milestone-escrow/src/admin_override_cancel_tests.rs index b76a787..11732f9 100644 --- a/contracts/milestone-escrow/src/admin_override_cancel_tests.rs +++ b/contracts/milestone-escrow/src/admin_override_cancel_tests.rs @@ -16,9 +16,12 @@ //! - Error::InvalidAmount is returned (not a panic) for edge-case amounts. //! - Terminal milestones are correctly skipped in every scenario. +#![cfg(test)] + use super::*; +use crate::test::setup_funded_escrow; use crate::{DataKey, Error, MilestoneEscrowClient, MilestoneStatus}; -use soroban_sdk::{token, vec, Address, Env}; +use soroban_sdk::{testutils::Address as _, token, vec, Address, Env}; // ──────────────────────────────────────────────────────────────────────────── // Issue #383: admin_override_cancel_release storage footprint @@ -118,8 +121,7 @@ fn test_cancel_release_requires_cancel_lock() { let env = Env::default(); env.mock_all_auths(); - let (_, _, _, admin_addr, _, _, client) = - setup_funded_escrow(&env, vec![&env, 1_000_i128]); + let (_, _, _, admin_addr, _, _, client) = setup_funded_escrow(&env, vec![&env, 1_000_i128]); let result = client.try_admin_override_cancel_release(&admin_addr); assert_eq!(result, Err(Ok(Error::InvalidStatus))); @@ -131,8 +133,7 @@ fn test_cancel_release_unauthorized_caller_rejected() { let env = Env::default(); env.mock_all_auths(); - let (client_addr, _, _, _, _, _, client) = - setup_funded_escrow(&env, vec![&env, 1_000_i128]); + let (client_addr, _, _, _, _, _, client) = setup_funded_escrow(&env, vec![&env, 1_000_i128]); client.cancel_escrow(&client_addr); let attacker = Address::generate(&env); @@ -225,8 +226,7 @@ fn test_cancel_refund_requires_cancel_lock() { let env = Env::default(); env.mock_all_auths(); - let (_, _, _, admin_addr, _, _, client) = - setup_funded_escrow(&env, vec![&env, 1_000_i128]); + let (_, _, _, admin_addr, _, _, client) = setup_funded_escrow(&env, vec![&env, 1_000_i128]); let result = client.try_admin_override_cancel_refund(&admin_addr); assert_eq!(result, Err(Ok(Error::InvalidStatus))); @@ -324,8 +324,7 @@ fn test_cancel_refund_multiple_milestones_sum_correctly() { env.mock_all_auths(); let amounts = vec![&env, 100_i128, 200_i128, 300_i128, 400_i128]; - let (client_addr, _, _, admin_addr, token_id, _, client) = - setup_funded_escrow(&env, amounts); + let (client_addr, _, _, admin_addr, token_id, _, client) = setup_funded_escrow(&env, amounts); client.cancel_escrow(&client_addr); @@ -357,4 +356,3 @@ fn test_cancel_refund_minimum_valid_amount() { assert_eq!(token.balance(&client_addr), client_before + 1); } - diff --git a/contracts/milestone-escrow/src/test.rs b/contracts/milestone-escrow/src/test.rs index 967e826..82ec99b 100644 --- a/contracts/milestone-escrow/src/test.rs +++ b/contracts/milestone-escrow/src/test.rs @@ -165,7 +165,7 @@ impl ReentrantToken { } } -fn setup_funded_escrow( +pub(crate) fn setup_funded_escrow( env: &Env, milestone_amounts: soroban_sdk::Vec, ) -> ( @@ -7487,7 +7487,8 @@ fn test_multisig_approval_init_succeeds() { } /// Initialisation: a second call to `multisig_approval_init` must be rejected -/// with `AlreadyInitialized`. +/// with `AlreadyInitialized` (the illegal source state for this one-time +/// setup function), and no storage entry is mutated by the rejected call. #[test] fn test_multisig_approval_init_duplicate_fails() { let env = Env::default(); @@ -7496,9 +7497,18 @@ fn test_multisig_approval_init_duplicate_fails() { let (client, admin, _signers) = setup_multisig(&env, 2); let extra = Address::generate(&env); - let new_signers = vec![&env, extra]; + let new_signers = vec![&env, extra.clone()]; let result = client.try_multisig_approval_init(&admin, &new_signers, &1u32); assert_eq!(result, Err(Ok(Error::AlreadyInitialized))); + + // No storage entry was mutated: the original threshold (2, not the + // attempted 1) is still in effect. + let state = client.try_is_multisig_approved(&0u32).unwrap().unwrap(); + assert_eq!(state.threshold, 2); + + // The new signer from the rejected call was never written either. + let approve_result = client.try_multisig_approve(&extra, &0u32); + assert_eq!(approve_result, Err(Ok(Error::Unauthorized))); } /// Initialisation: zero signers must be rejected. @@ -7787,7 +7797,9 @@ fn test_multisig_approve_emits_structured_event() { assert_eq!(matched, 1); } -/// Admin: unauthorised caller cannot initialise multisig. +/// Admin: unauthorised caller cannot initialise multisig, and no storage +/// entry is mutated by the rejected attempt (the original signer set and +/// threshold from `setup_multisig` remain in effect). #[test] fn test_multisig_approval_init_unauthorized_fails() { let env = Env::default(); @@ -7799,6 +7811,16 @@ fn test_multisig_approval_init_unauthorized_fails() { let new_signers = vec![&env, impostor.clone()]; let result = client.try_multisig_approval_init(&impostor, &new_signers, &1u32); assert_eq!(result, Err(Ok(Error::Unauthorized))); + + // No storage entry was mutated: the original threshold (2, not the + // impostor's attempted 1) is still in effect. + let state = client.try_is_multisig_approved(&0u32).unwrap().unwrap(); + assert_eq!(state.threshold, 2); + + // The impostor was never written into the signer set either: they + // cannot approve as if they were a registered signer. + let approve_result = client.try_multisig_approve(&impostor, &0u32); + assert_eq!(approve_result, Err(Ok(Error::Unauthorized))); } // ============================================================================