Skip to content
Merged
Show file tree
Hide file tree
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
33 changes: 33 additions & 0 deletions src/component/transfer/mock.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use starknet::ContractAddress;

#[starknet::contract]
pub mod MockTransferContract {
use starkremit_contract::component::transfer::transfer::transfer_component;
use super::*;

component!(path: transfer_component, storage: transfer_component, event: TransferEvent);

#[abi(embed_v0)]
impl TransferImpl = transfer_component::Transfer<ContractState>;

// Event definitions
#[event]
#[derive(Drop, starknet::Event)]
pub enum Event {
#[flat]
TransferEvent: transfer_component::Event,
}

// Contract storage definition
#[storage]
#[allow(starknet::colliding_storage_paths)]
struct Storage {
#[substorage(v0)]
transfer_component: transfer_component::Storage,
}

// Contract constructor
#[constructor]
fn constructor(ref self: ContractState) { // No initialization needed for transfer component
}
}
260 changes: 260 additions & 0 deletions src/component/transfer/test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
#[cfg(test)]
mod transfer_tests {
use snforge_std::{
ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, declare, spy_events,
start_cheat_block_timestamp, start_cheat_caller_address, stop_cheat_caller_address,
};
use starknet::{ContractAddress, contract_address_const};
use starkremit_contract::base::types::TransferStatus;
use starkremit_contract::component::transfer::transfer::{
ITransferDispatcher, ITransferDispatcherTrait, transfer_component,
};

pub fn SENDER() -> ContractAddress {
contract_address_const::<'SENDER'>()
}

pub fn RECIPIENT() -> ContractAddress {
contract_address_const::<'RECIPIENT'>()
}

pub fn ADMIN() -> ContractAddress {
contract_address_const::<'ADMIN'>()
}

fn deploy_transfer_contract() -> (ContractAddress, ITransferDispatcher) {
let transfer_class_hash = declare("MockTransferContract").unwrap().contract_class();
let mut constructor_calldata = array![];
let (contract_address, _) = transfer_class_hash.deploy(@constructor_calldata).unwrap();

let transfer_dispatcher = ITransferDispatcher { contract_address: contract_address };

(contract_address, transfer_dispatcher)
}

#[test]
fn test_process_expired_transfers_success() {
let (contract_address, transfer_dispatcher) = deploy_transfer_contract();
let mut spy = spy_events();

// Create a transfer that will expire
let transfer_amount = 1000;
let current_time = 1000000000; // Current timestamp
let expiry_time = current_time + 3600; // Expires in 1 hour

// Set up the transfer
start_cheat_caller_address(contract_address, SENDER());
start_cheat_block_timestamp(contract_address, current_time);
let transfer_id = transfer_dispatcher
.initiate_transfer(RECIPIENT(), transfer_amount, expiry_time, 'metadata');
stop_cheat_caller_address(contract_address);

// Verify transfer is initially pending
let transfer = transfer_dispatcher.get_transfer(transfer_id);
assert!(transfer.status == TransferStatus::Pending, "Transfer should be pending");
assert!(transfer.expires_at == expiry_time, "Expiry time should match");

// Fast forward time to after expiry
let future_time = expiry_time + 1;
start_cheat_block_timestamp(contract_address, future_time);

// Process expired transfers
let processed_count = transfer_dispatcher.process_expired_transfers(10);
assert!(processed_count == 1, "Should process 1 expired transfer");

// Verify transfer is now expired
let expired_transfer = transfer_dispatcher.get_transfer(transfer_id);
assert!(expired_transfer.status == TransferStatus::Expired, "Transfer should be expired");
assert!(expired_transfer.updated_at == future_time, "Updated time should be future time");

// Verify statistics
let (total, _completed, _cancelled, expired) = transfer_dispatcher
.get_transfer_statistics();
assert!(total == 1, "Total transfers should be 1");
assert!(expired == 1, "Expired transfers should be 1");
}

#[test]
fn test_process_multiple_expired_transfers() {
let (contract_address, transfer_dispatcher) = deploy_transfer_contract();
let mut _spy = spy_events();

let current_time = 1000000000;
let expiry_time = current_time + 3600; // Expires in 1 hour

// Create multiple transfers
start_cheat_caller_address(contract_address, SENDER());
start_cheat_block_timestamp(contract_address, current_time);

let transfer_id1 = transfer_dispatcher
.initiate_transfer(RECIPIENT(), 1000, expiry_time, 'metadata1');

let transfer_id2 = transfer_dispatcher
.initiate_transfer(RECIPIENT(), 2000, expiry_time, 'metadata2');

let transfer_id3 = transfer_dispatcher
.initiate_transfer(
RECIPIENT(), 3000, expiry_time + 7200, // This one expires later
'metadata3',
);

stop_cheat_caller_address(contract_address);

// Fast forward time to after first two transfers expire
let future_time = expiry_time + 1;
start_cheat_block_timestamp(contract_address, future_time);

// Process expired transfers with limit of 5
let processed_count = transfer_dispatcher.process_expired_transfers(5);
assert!(processed_count == 2, "Should process 2 expired transfers");

// Verify first two transfers are expired
let transfer1 = transfer_dispatcher.get_transfer(transfer_id1);
let transfer2 = transfer_dispatcher.get_transfer(transfer_id2);
let transfer3 = transfer_dispatcher.get_transfer(transfer_id3);

assert!(transfer1.status == TransferStatus::Expired, "Transfer 1 should be expired");
assert!(transfer2.status == TransferStatus::Expired, "Transfer 2 should be expired");
assert!(transfer3.status == TransferStatus::Pending, "Transfer 3 should still be pending");

// Verify statistics
let (total, _completed, _cancelled, expired) = transfer_dispatcher
.get_transfer_statistics();
assert!(total == 3, "Total transfers should be 3");
assert!(expired == 2, "Expired transfers should be 2");
}

#[test]
#[should_panic(expected: ('Invalid transfer status',))]
fn test_complete_transfer_after_expiry_panics() {
let (contract_address, transfer_dispatcher) = deploy_transfer_contract();

let current_time = 1000000000;
let expiry_time = current_time + 3600; // Expires in 1 hour

// Create a transfer
start_cheat_caller_address(contract_address, SENDER());
start_cheat_block_timestamp(contract_address, current_time);
let transfer_id = transfer_dispatcher
.initiate_transfer(RECIPIENT(), 1000, expiry_time, 'metadata');
stop_cheat_caller_address(contract_address);

// Fast forward time to after expiry
let future_time = expiry_time + 1;
start_cheat_block_timestamp(contract_address, future_time);

// Process expired transfers to mark as expired
transfer_dispatcher.process_expired_transfers(10);

// Try to complete the expired transfer - this should panic
start_cheat_caller_address(contract_address, RECIPIENT());
transfer_dispatcher.complete_transfer(transfer_id);
stop_cheat_caller_address(contract_address);
}

#[test]
#[should_panic(expected: ('Invalid transfer status',))]
fn test_partial_complete_transfer_after_expiry_panics() {
let (contract_address, transfer_dispatcher) = deploy_transfer_contract();

let current_time = 1000000000;
let expiry_time = current_time + 3600; // Expires in 1 hour

// Create a transfer
start_cheat_caller_address(contract_address, SENDER());
start_cheat_block_timestamp(contract_address, current_time);
let transfer_id = transfer_dispatcher
.initiate_transfer(RECIPIENT(), 1000, expiry_time, 'metadata');
stop_cheat_caller_address(contract_address);

// Fast forward time to after expiry
let future_time = expiry_time + 1;
start_cheat_block_timestamp(contract_address, future_time);

// Process expired transfers to mark as expired
transfer_dispatcher.process_expired_transfers(10);

// Try to partially complete the expired transfer - this should panic
start_cheat_caller_address(contract_address, RECIPIENT());
transfer_dispatcher.partial_complete_transfer(transfer_id, 500);
stop_cheat_caller_address(contract_address);
}

#[test]
#[should_panic(expected: ('Invalid transfer status',))]
fn test_request_cash_out_after_expiry_panics() {
let (contract_address, transfer_dispatcher) = deploy_transfer_contract();

let current_time = 1000000000;
let expiry_time = current_time + 3600; // Expires in 1 hour

// Create a transfer
start_cheat_caller_address(contract_address, SENDER());
start_cheat_block_timestamp(contract_address, current_time);
let transfer_id = transfer_dispatcher
.initiate_transfer(RECIPIENT(), 1000, expiry_time, 'metadata');
stop_cheat_caller_address(contract_address);

// Fast forward time to after expiry
let future_time = expiry_time + 1;
start_cheat_block_timestamp(contract_address, future_time);

// Process expired transfers to mark as expired
transfer_dispatcher.process_expired_transfers(10);

// Try to request cash out for the expired transfer - this should panic
start_cheat_caller_address(contract_address, RECIPIENT());
transfer_dispatcher.request_cash_out(transfer_id);
stop_cheat_caller_address(contract_address);
}

#[test]
fn test_process_expired_transfers_with_limit() {
let (contract_address, transfer_dispatcher) = deploy_transfer_contract();

let current_time = 1000000000;
let expiry_time = current_time + 3600; // Expires in 1 hour

// Create 5 transfers that will expire
start_cheat_caller_address(contract_address, SENDER());
start_cheat_block_timestamp(contract_address, current_time);

let mut i: u32 = 0;
while i != 5 {
let _transfer_id = transfer_dispatcher
.initiate_transfer(RECIPIENT(), 1000, expiry_time, 'metadata');
i += 1;
}

stop_cheat_caller_address(contract_address);

// Fast forward time to after expiry
let future_time = expiry_time + 1;
start_cheat_block_timestamp(contract_address, future_time);

// Process only 3 expired transfers (limit)
let processed_count = transfer_dispatcher.process_expired_transfers(3);
assert!(processed_count == 3, "Should process exactly 3 expired transfers");

// Verify statistics
let (total, _completed, _cancelled, expired) = transfer_dispatcher
.get_transfer_statistics();
assert!(total == 5, "Total transfers should be 5");
assert!(expired == 3, "Expired transfers should be 3");
}

#[test]
fn test_no_transfers_to_expire() {
let (_contract_address, transfer_dispatcher) = deploy_transfer_contract();

// Process expired transfers when no transfers exist
let processed_count = transfer_dispatcher.process_expired_transfers(10);
assert!(processed_count == 0, "Should process 0 expired transfers");

// Verify statistics remain at 0
let (total, _completed, _cancelled, expired) = transfer_dispatcher
.get_transfer_statistics();
assert!(total == 0, "Total transfers should be 0");
assert!(expired == 0, "Expired transfers should be 0");
}
}
Loading
Loading