This document describes the test coverage for the cancel_stream entrypoint's refund splitting logic. The tests verify that when a stream is cancelled, funds are correctly split between the sender and recipient based on the vested amount at cancellation time.
When a stream is cancelled, funds are split as follows:
- Recipient receives:
vested_amount - released_amount(accrued but not yet withdrawn) - Sender receives:
total_amount - vested_amount(unvested/unstreamed)
This ensures the recipient is entitled to everything that has already vested, regardless of whether they have withdrawn it.
contracts/contracts/streampay-stream/tests/refund.rs
- Test:
cancel_immediately_refunds_all_to_sender - Scenario: Stream cancelled before start_time
- Expected: Full amount refunded to sender, recipient receives nothing
- Coverage: Verifies zero vested amount handling
- Test:
cancel_after_full_vesting_pays_all_to_recipient - Scenario: Stream cancelled after end_time (fully vested)
- Expected: Full amount paid to recipient, sender receives nothing
- Coverage: Verifies 100% vested amount handling
- Test:
cancel_with_partial_vesting_splits_correctly - Scenario: Stream cancelled at 50% vested, no withdrawals made
- Expected: 50% to recipient, 50% to sender
- Coverage: Verifies proportional split at intermediate point
- Test:
cancel_with_withdrawals_pays_vested_minus_released - Scenario: Stream cancelled at 50% vested, 200k already withdrawn
- Expected: Recipient gets 300k (vested - released), sender gets 500k (unvested)
- Coverage: Verifies vested minus released calculation
- Test:
cancel_after_full_withdrawal_refunds_nothing - Scenario: Stream cancelled after full amount withdrawn
- Expected: Neither party receives additional funds
- Coverage: Verifies no double-payment when fully withdrawn
- Test:
cancel_paused_stream_splits_correctly - Scenario: Stream paused at 1/3 vested, then cancelled
- Expected: Split based on vested amount at pause time
- Coverage: Verifies paused stream handling (accrual stops at pause)
- Test:
cancel_draft_stream_refunds_all_to_sender - Scenario: Stream cancelled before start_time (effectively draft)
- Expected: Full amount refunded to sender
- Coverage: Verifies pre-start cancellation handling
- Test:
cancel_with_small_amounts_handles_correctly - Scenario: Stream with total_amount = 1
- Expected: No arithmetic errors, correct split
- Coverage: Verifies handling of minimum amounts
- Test:
cancel_with_large_amounts_no_overflow - Scenario: Stream with total_amount = i128::MAX / 2
- Expected: No overflow, correct split
- Coverage: Verifies overflow-safe arithmetic
- Test:
cancel_with_multiple_withdrawals - Scenario: Two withdrawals before cancellation
- Expected: Correct calculation of remaining vested amount
- Coverage: Verifies cumulative released amount handling
-
Test:
cancel_at_start_time -
Scenario: Cancel exactly at start_time
-
Expected: 0 vested, full refund to sender
-
Coverage: Verifies boundary condition at start
-
Test:
cancel_at_end_time -
Scenario: Cancel exactly at end_time
-
Expected: Fully vested, full payout to recipient
-
Coverage: Verifies boundary condition at end
- Test:
cancel_already_settled_fails - Scenario: Attempt to cancel a settled stream
- Expected: Operation fails with error
- Coverage: Verifies state transition guard
- Test:
cancel_already_cancelled_fails - Scenario: Attempt to cancel an already cancelled stream
- Expected: Operation fails with error
- Coverage: Verifies idempotency guard
- Test:
cancel_nonexistent_stream_fails - Scenario: Attempt to cancel non-existent stream_id
- Expected: Operation fails with error
- Coverage: Verifies existence check
- Total Tests: 14
- Boundary Condition Tests: 7
- Edge Case Tests: 5
- Error Case Tests: 3
The test suite aims to achieve:
- Line Coverage: >95% of
cancel_streamimplementation - Branch Coverage: All arithmetic branches (vested calculation, refund split)
- State Coverage: All stream states (Active, Paused, Draft, Settled, Cancelled)
- Boundary Coverage: 0%, 50%, 100% vested amounts
- Error Coverage: All error paths in
cancel_stream
cd contracts/contracts/streampay-stream
cargo test refundThe test suite provides helper functions:
setup_refund_test(): Sets up test environment with deployed contract and funded sendercreate_stream(): Creates a stream with given parametersget_balance(): Gets token balance of an address
struct RefundTestData {
env: Env,
admin: Address,
sender: Address,
recipient: Address,
token: Address,
client: ContractClient<'static>,
}Each test verifies the refund split by:
- Recording balances before cancellation
- Executing
cancel_stream - Recording balances after cancellation
- Asserting expected payout amounts
- Verifying stream status is
Cancelled
- Paused Duration: Tests do not cover resume after pause followed by cancel
- Amend + Cancel: Tests do not cover stream amendment followed by cancellation
- Concurrent Operations: Tests do not cover concurrent withdrawals and cancellation
- Multiple Streams: Tests focus on single-stream scenarios
Potential additional test coverage:
- Resume After Pause: Cancel after pause → resume → cancel
- Amend Then Cancel: Cancel after stream amendment
- Extreme Durations: Very short (1 second) and very long (years) streams
- Token Precision: Test with tokens having different decimal precisions
- Gas Measurement: Measure gas cost of cancel operations