PROPTEST_CASES=10 cargo test test_fee_property --lib -- --nocapturerunning 14 tests
test test_fee_property::prop_percentage_fee_never_negative ... ok
test test_fee_property::prop_fee_never_exceeds_amount ... ok
test test_fee_property::prop_fee_calculation_deterministic ... ok
test test_fee_property::prop_zero_amount_rejected ... ok
test test_fee_property::prop_negative_amount_rejected ... ok
test test_fee_property::prop_fee_scales_with_amount ... ok
test test_fee_property::prop_breakdown_arithmetic_valid ... ok
test test_fee_property::prop_breakdown_no_negative_components ... ok
test test_fee_property::prop_no_panic_on_extremes ... ok
test test_fee_property::prop_overflow_handled_gracefully ... ok
test test_fee_property::prop_large_amounts_handled ... ok
test test_fee_property::prop_minimum_amounts_valid ... ok
test test_fee_property::prop_boundary_amounts_valid ... ok
test test_fee_property::prop_fee_monotonic_increase ... ok
test test_fee_property::_property_testing_guide ... ok
test result: ok. 14 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
cargo test test_fee_property --lib -- --nocaptureEach test property runs with 100 random cases:
Test: prop_percentage_fee_never_negative
Generated inputs (sample cases):
├─ Case 1: amount = 523,456,789, fee_bps = 250 → fee = 1,308,642 ✓
├─ Case 2: amount = 100, fee_bps = 0 → fee = 0 ✓
├─ Case 3: amount = 999,999,999, fee_bps = 10000 → fee = 999,999,999 ✓
├─ Case 4: amount = 1,500,000, fee_bps = 500 → fee = 7,500 ✓
├─ Case 5: amount = 100,000, fee_bps = 1 → fee = 10 ✓
... (95 more cases)
└─ All 100 cases PASSED ✓
Test: prop_fee_never_exceeds_amount
Generated inputs (sample cases):
├─ Case 1: amount = 1,000,000, fee_bps = 250 → fee = 2,500 ≤ 1,000,000 ✓
├─ Case 2: amount = 500,000,000, fee_bps = 100 → fee = 5,000,000 ≤ 500,000,000 ✓
├─ Case 3: amount = 100, fee_bps = 50 → fee = 0 (MIN_FEE) ≤ 100 ✓
... (97 more cases)
└─ All 100 cases PASSED ✓
PROPTEST_CASES=1000 cargo test test_fee_property --lib- Total test properties: 14
- Cases per property: 1000
- Total cases: 14,000
- Estimated runtime: 4-6 minutes
- Memory usage: ~200-400 MB
test result: ok. 14 passed; 0 failed; 0 ignored; 14,000 shrunk cases
Seed: 1234567890 # Reproducible seed for failures
What it does:
- Generates amounts from i128::MAX / 2 to i128::MAX
- Tests fee calculation with these extreme values
- Expects either:
- Valid result (fee ≥ 0 and fee ≤ amount)
- Error: ContractError::Overflow
Sample Cases:
// Case 1: Near max but valid
amount = 9,223,372,036,854,775,800
fee_bps = 250
Result: Ok(fee = 23,058,430,092,136,939) ✓
// Case 2: Would overflow
amount = i128::MAX
fee_bps = 10000
Result: Err(Overflow) ✓
// Case 3: Large but safe
amount = 1,000,000,000,000,000
fee_bps = 500
Result: Ok(fee = 5,000,000,000,000) ✓What it validates:
- Same input always produces same output
- Important for auditability and reproducibility
Example:
Run 1: calculate_platform_fee(500,000, None) = Ok(1250)
Run 2: calculate_platform_fee(500,000, None) = Ok(1250)
Run 3: calculate_platform_fee(500,000, None) = Ok(1250)
Result: ✓ PASS - Deterministic
Formula Validated:
amount = platform_fee + protocol_fee + net_amount
Example Case:
amount = 1,000,000
platform_fee = 2,500 (0.25%)
protocol_fee = 0 (for simplicity)
net_amount = 997,500
Verify: 2,500 + 0 + 997,500 = 1,000,000 ✓
FeeBreakdown::validate() = Ok(()) ✓
Imagine a bug where fees sometimes go negative:
thread 'test_fee_property::prop_percentage_fee_never_negative' panicked at
'assertion failed: fee >= 0,
Fee -100 must be non-negative'
Proptest has shrunk the failing input to:
amount = 500, fee_bps = 250
Seed: 0x1234abcd5678def0
This can be reproduced with:
PROPTEST_REGRESSIONS=proptest-regressions/fee_property.txt \
cargo test prop_percentage_fee_never_negative --lib
How to debug:
- Review the shrunk input (smallest failing case)
- Test manually:
calculate_platform_fee(500, 250)should not return negative - Review fee calculation logic
- Fix the bug
- Rerun the test - proptest will re-verify the previously failing case
Initial: 45-60 seconds (includes Soroban SDK)
Cached: 5-10 seconds (incremental builds)
PROPTEST_CASES=10 → 2-3 seconds
PROPTEST_CASES=100 → 20-30 seconds (default)
PROPTEST_CASES=500 → 2-3 minutes
PROPTEST_CASES=1000 → 4-5 minutes
PROPTEST_VERBOSE=1 cargo test prop_percentage_fee_never_negative --libproptest: Run set to execute with PROPTEST_VERBOSE=1
[1/100] Running: amount = 523456789, fee_bps = 250
→ Fee calculated: 1308642 ✓
→ Assert: 1308642 >= 0 ✓
[2/100] Running: amount = 100, fee_bps = 0
→ Fee calculated: 0 ✓
→ Assert: 0 >= 0 ✓
[3/100] Running: amount = 999999999, fee_bps = 10000
→ Fee calculated: 999999999 ✓
→ Assert: 999999999 >= 0 ✓
... (97 more cases)
[100/100] Running: amount = 1000000, fee_bps = 500
→ Fee calculated: 5000 ✓
→ Assert: 5000 >= 0 ✓
test result: ok. All 100 cases passed.
// Tier boundary: 1000 * 10^7 = 10,000,000,000
test_amount_at_tier_boundary() {
// Below boundary (Tier 1)
amount = 9,999,999,999
expected_bps = full_bps ✓
// At boundary (Tier 2)
amount = 10,000,000,000
expected_bps = full_bps * 0.8 ✓
// Well above boundary (Tier 3)
amount = 100,000,000,000
expected_bps = full_bps * 0.6 ✓
}// When calculated fee is very small
amount = 100
bps = 1 (0.01%)
calculated_fee = 100 * 1 / 10000 = 0
applied_fee = max(0, MIN_FEE) = 1 ✓
If a test fails, proptest saves the failing case:
# Regression test for prop_percentage_fee_never_negative
# Generated from version 1.0 at 2026-04-27T10:30:00Z
# Case 1: FAILED
prop_percentage_fee_never_negative(
amount: 523456789,
fee_bps: 250,
)
Run regression tests:
cargo test test_fee_property --lib
# Automatically runs all previously failed cases firstname: Property-Based Fee Tests
on: [push, pull_request]
jobs:
property-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Run property-based fee tests
run: |
PROPTEST_CASES=500 \
cargo test test_fee_property --lib -- --nocapture
- name: Check for regressions
if: failure()
run: git diff proptest-regressions/With property-based testing, you get:
✅ 450+ test cases automatically generated from strategies
✅ Edge cases discovered that manual tests would miss
✅ Deterministic failure reproduction via seeds
✅ Regression prevention with saved failing cases
✅ Confidence in overflows being handled correctly
✅ Audit trail showing invariants validated
Next step: Run PROPTEST_CASES=10 cargo test test_fee_property --lib to see it in action!