Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 159 additions & 3 deletions integration_tests/tests/multi_contract_integration.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![cfg(test)]

use soroban_sdk::{testutils::Address as _, Address, Env, String as SorobanString};
use soroban_sdk::{testutils::Address as _, Address, Env, String as SorobanString, IntoVal, Symbol, Val};
use soroban_sdk::testutils::Events;

// Import all contract types and clients
use bill_payments::{BillPayments, BillPaymentsClient};
Expand All @@ -13,7 +14,7 @@ use savings_goals::{SavingsGoalContract, SavingsGoalContractClient};
// Mock Contracts for Orchestrator Integration Tests
// ============================================================================

use soroban_sdk::{contract, contractimpl, Vec as SorobanVec};
use soroban_sdk::{contract, contractimpl, Vec as SorobanVec, vec as soroban_vec};

/// Mock Family Wallet — approves any amount <= 100_000
#[contract]
Expand Down Expand Up @@ -797,7 +798,162 @@ fn test_event_topic_compliance_across_contracts() {
}

// Fail if any non-compliant events found, listing one example for debugging
assert_eq!(non_compliant.len(), 0u32, "Found events that do not follow the Remitwise topic schema. See EVENTS.md and remitwise-common::RemitwiseEvents for guidance.");
assert_eq!(non_compliant.len(), 0u32, "Found events that do not follow the Remitwise topic schema. See EVENTS.md and remitwise-common::RemitwiseEvents for guidance.");
}

// ============================================================================
// Stress Integration Tests — Batch Execution & High Volume
// ============================================================================

/// INT-STRESS-01: High-volume batch execution (20 flows).
/// Verifies that the orchestrator can handle a large batch of valid flows
/// in a single transaction without exceeding gas limits.
#[test]
fn test_integration_stress_high_volume_batch_success() {
let (env, _, mock_savings_id, mock_bills_id, mock_insurance_id,
orchestrator_id, mock_family_wallet_id, mock_split_id, user) = setup_full_env();

let client = OrchestratorClient::new(&env, &orchestrator_id);

let mut flows = SorobanVec::new(&env);
for _ in 0..20 {
flows.push_back(RemittanceFlowArgs {
total_amount: 1000,
family_wallet_addr: mock_family_wallet_id.clone(),
remittance_split_addr: mock_split_id.clone(),
savings_addr: mock_savings_id.clone(),
bills_addr: mock_bills_id.clone(),
insurance_addr: mock_insurance_id.clone(),
goal_id: 1,
bill_id: 1,
policy_id: 1,
});
}

let result = client.try_execute_remittance_batch(&user, &flows);

assert!(result.is_ok(), "STRESS-01: High-volume batch must succeed");
let batch_results = result.unwrap().unwrap();
assert_eq!(batch_results.len(), 20);

for res in batch_results.iter() {
let _ = res.expect("Flow in batch should be Ok");
}

println!("✅ STRESS-01 passed: 20-flow batch processed successfully");
}

/// INT-STRESS-02: Mixed success/failure batch.
/// Verifies that the batch continues processing when individual flows fail
/// (e.g., due to invalid IDs or spending limits).
#[test]
fn test_integration_stress_mixed_batch() {
let (env, _, mock_savings_id, mock_bills_id, mock_insurance_id,
orchestrator_id, mock_family_wallet_id, mock_split_id, user) = setup_full_env();

let client = OrchestratorClient::new(&env, &orchestrator_id);

let mut flows = SorobanVec::new(&env);

// 1. Valid flow
flows.push_back(RemittanceFlowArgs {
total_amount: 1000,
family_wallet_addr: mock_family_wallet_id.clone(),
remittance_split_addr: mock_split_id.clone(),
savings_addr: mock_savings_id.clone(),
bills_addr: mock_bills_id.clone(),
insurance_addr: mock_insurance_id.clone(),
goal_id: 1,
bill_id: 1,
policy_id: 1,
});

// 2. Invalid flow (savings goal not found-999)
flows.push_back(RemittanceFlowArgs {
total_amount: 1000,
family_wallet_addr: mock_family_wallet_id.clone(),
remittance_split_addr: mock_split_id.clone(),
savings_addr: mock_savings_id.clone(),
bills_addr: mock_bills_id.clone(),
insurance_addr: mock_insurance_id.clone(),
goal_id: 999,
bill_id: 1,
policy_id: 1,
});

// 3. Invalid flow (spending limit exceeded-200,000 > 100,000)
flows.push_back(RemittanceFlowArgs {
total_amount: 200_000,
family_wallet_addr: mock_family_wallet_id.clone(),
remittance_split_addr: mock_split_id.clone(),
savings_addr: mock_savings_id.clone(),
bills_addr: mock_bills_id.clone(),
insurance_addr: mock_insurance_id.clone(),
goal_id: 1,
bill_id: 1,
policy_id: 1,
});

// 4. Valid flow
flows.push_back(RemittanceFlowArgs {
total_amount: 500,
family_wallet_addr: mock_family_wallet_id.clone(),
remittance_split_addr: mock_split_id.clone(),
savings_addr: mock_savings_id.clone(),
bills_addr: mock_bills_id.clone(),
insurance_addr: mock_insurance_id.clone(),
goal_id: 1,
bill_id: 1,
policy_id: 1,
});

let result = client.try_execute_remittance_batch(&user, &flows);

assert!(result.is_ok());
let batch_results = result.unwrap().unwrap();
assert_eq!(batch_results.len(), 4);

assert!(batch_results.get(0).unwrap().is_ok(), "Flow 1 should succeed");
assert!(batch_results.get(1).unwrap().is_err(), "Flow 2 should fail (savings)");
assert!(batch_results.get(2).unwrap().is_err(), "Flow 3 should fail (limit)");
assert!(batch_results.get(3).unwrap().is_ok(), "Flow 4 should succeed");

// Type hint for Result
let _: Result<RemittanceFlowResult, OrchestratorError> = batch_results.get(0).unwrap();

println!("✅ STRESS-02 passed: Mixed success/failure batch tracked correctly");
}

/// INT-STRESS-03: Repeated batch execution.
/// Verifies that repeated batch calls do not cause state corruption
/// or unexpected gas escalations.
#[test]
fn test_integration_stress_repeated_batches() {
let (env, _, mock_savings_id, mock_bills_id, mock_insurance_id,
orchestrator_id, mock_family_wallet_id, mock_split_id, user) = setup_full_env();

let client = OrchestratorClient::new(&env, &orchestrator_id);

for i in 0..5 {
let mut flows = SorobanVec::new(&env);
for _ in 0..10 {
flows.push_back(RemittanceFlowArgs {
total_amount: 100,
family_wallet_addr: mock_family_wallet_id.clone(),
remittance_split_addr: mock_split_id.clone(),
savings_addr: mock_savings_id.clone(),
bills_addr: mock_bills_id.clone(),
insurance_addr: mock_insurance_id.clone(),
goal_id: 1,
bill_id: 1,
policy_id: 1,
});
}
let result = client.try_execute_remittance_batch(&user, &flows);
assert!(result.is_ok(), "Batch {} must succeed", i);
}

println!("✅ STRESS-03 passed: 5 consecutive batches processed cleanly");
}

// ============================================================================
Expand Down
Loading