From f93f5eb4ef65130094bf69c79a633e4c4f77d3e7 Mon Sep 17 00:00:00 2001 From: Elisha Suleiman <112385548+lishmanTech@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:55:38 +0000 Subject: [PATCH] test: add property-based tests for drip contract invariants --- contracts/tests/property_based.rs | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 contracts/tests/property_based.rs diff --git a/contracts/tests/property_based.rs b/contracts/tests/property_based.rs new file mode 100644 index 0000000..ae72bcd --- /dev/null +++ b/contracts/tests/property_based.rs @@ -0,0 +1,64 @@ +use grant_stream::DripContract; // adjust to your actual contract module +use proptest::prelude::*; + +// Helper enum for randomized actions +#[derive(Debug, Clone)] +enum Action { + Pause, + Resume, + Withdraw(u128), + ChangeRate(u128), +} + +// Property-based test +proptest! { + #[test] + fn drip_invariants(actions in prop::collection::vec( + prop_oneof![ + Just(Action::Pause), + Just(Action::Resume), + (1u128..1000u128).prop_map(Action::Withdraw), + (1u128..100u128).prop_map(Action::ChangeRate), + ], + 1..50 // number of actions in the sequence + )) { + // Initialize the contract with a random deposit + let initial_deposit = 10000u128; + let mut drip = DripContract::new(initial_deposit, 10); // 10 tokens per block rate, example + let mut total_withdrawn = 0u128; + let mut accrued_while_paused = 0u128; + let mut is_paused = false; + + for action in actions { + match action { + Action::Pause => { + drip.pause(); + is_paused = true; + } + Action::Resume => { + drip.resume(); + is_paused = false; + } + Action::Withdraw(amount) => { + let withdrawn = drip.withdraw(amount); + total_withdrawn += withdrawn; + } + Action::ChangeRate(new_rate) => { + drip.set_rate(new_rate); + } + } + + // Assert that during paused state, accrued doesn't increase + if is_paused { + let accrued = drip.accrued_balance(); + prop_assert_eq!(accrued, accrued_while_paused); + } else { + accrued_while_paused = drip.accrued_balance(); + } + + // Assert invariant: total withdrawn + remaining balance == initial deposit + let remaining = drip.remaining_balance(); + prop_assert_eq!(total_withdrawn + remaining, initial_deposit); + } + } +} \ No newline at end of file