From 98ef8ec9c38d2edb5f3fb09e84a3905346b204fd Mon Sep 17 00:00:00 2001 From: thesn10 <38666407+thesn10@users.noreply.github.com> Date: Mon, 18 May 2026 12:26:18 +0200 Subject: [PATCH] split tests in multiple files --- programs/descro/tests/common/mod.rs | 173 +++++++ programs/descro/tests/escrow_tests.rs | 525 -------------------- programs/descro/tests/test_cancel.rs | 40 ++ programs/descro/tests/test_complete.rs | 62 +++ programs/descro/tests/test_create_escrow.rs | 63 +++ programs/descro/tests/test_deposit.rs | 59 +++ programs/descro/tests/test_dispute.rs | 64 +++ programs/descro/tests/test_resolve.rs | 118 +++++ 8 files changed, 579 insertions(+), 525 deletions(-) create mode 100644 programs/descro/tests/common/mod.rs delete mode 100644 programs/descro/tests/escrow_tests.rs create mode 100644 programs/descro/tests/test_cancel.rs create mode 100644 programs/descro/tests/test_complete.rs create mode 100644 programs/descro/tests/test_create_escrow.rs create mode 100644 programs/descro/tests/test_deposit.rs create mode 100644 programs/descro/tests/test_dispute.rs create mode 100644 programs/descro/tests/test_resolve.rs diff --git a/programs/descro/tests/common/mod.rs b/programs/descro/tests/common/mod.rs new file mode 100644 index 0000000..4d4a7eb --- /dev/null +++ b/programs/descro/tests/common/mod.rs @@ -0,0 +1,173 @@ +use { + anchor_lang::{ + solana_program::{instruction::Instruction, system_program}, + AccountDeserialize, InstructionData, ToAccountMetas, + }, + descro::{EscrowAccount, Winner}, + litesvm::LiteSVM, + solana_keypair::Keypair, + solana_message::{Message, VersionedMessage}, + solana_signer::Signer, + solana_transaction::versioned::VersionedTransaction, +}; +pub use anchor_lang::prelude::Pubkey; +pub use descro::{EscrowState, Winner as EscrowWinner}; + +pub const AMOUNT: u64 = 1_000_000_000; +pub const ESCROW_ID: u64 = 1; + +pub fn setup() -> (LiteSVM, Keypair, Keypair, Keypair) { + let program_id = descro::id(); + let mut svm = LiteSVM::new(); + let bytes = include_bytes!("../../../../target/deploy/descro.so"); + svm.add_program(program_id, bytes).unwrap(); + + let seller = Keypair::new(); + let buyer = Keypair::new(); + let resolver = Keypair::new(); + + svm.airdrop(&seller.pubkey(), 10_000_000_000).unwrap(); + svm.airdrop(&buyer.pubkey(), 10_000_000_000).unwrap(); + svm.airdrop(&resolver.pubkey(), 1_000_000_000).unwrap(); + + (svm, seller, buyer, resolver) +} + +pub fn escrow_pda(seller: &Pubkey, escrow_id: u64) -> Pubkey { + Pubkey::find_program_address( + &[b"escrow", seller.as_ref(), &escrow_id.to_le_bytes()], + &descro::id(), + ) + .0 +} + +pub fn vault_pda(escrow: &Pubkey) -> Pubkey { + Pubkey::find_program_address(&[b"vault", escrow.as_ref()], &descro::id()).0 +} + +pub fn send(svm: &mut LiteSVM, ix: Instruction, payer: &Keypair) { + let blockhash = svm.latest_blockhash(); + let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash); + let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[payer]).unwrap(); + svm.send_transaction(tx).expect("transaction failed"); +} + +pub fn try_send(svm: &mut LiteSVM, ix: Instruction, payer: &Keypair) -> bool { + let blockhash = svm.latest_blockhash(); + let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash); + let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[payer]).unwrap(); + svm.send_transaction(tx).is_ok() +} + +pub fn ix_create_escrow( + seller: &Pubkey, + buyer: &Pubkey, + amount: u64, + dispute_resolver: Option, + escrow_id: u64, +) -> Instruction { + let escrow = escrow_pda(seller, escrow_id); + let vault = vault_pda(&escrow); + Instruction::new_with_bytes( + descro::id(), + &descro::instruction::CreateEscrow { amount, dispute_resolver, escrow_id }.data(), + descro::accounts::CreateEscrow { + seller: *seller, + buyer: *buyer, + escrow_account: escrow, + vault, + system_program: system_program::ID, + } + .to_account_metas(None), + ) +} + +pub fn ix_deposit(buyer: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction { + let escrow = escrow_pda(seller, escrow_id); + let vault = vault_pda(&escrow); + Instruction::new_with_bytes( + descro::id(), + &descro::instruction::Deposit {}.data(), + descro::accounts::Deposit { + buyer: *buyer, + escrow_account: escrow, + vault, + system_program: system_program::ID, + } + .to_account_metas(None), + ) +} + +pub fn ix_complete(buyer: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction { + let escrow = escrow_pda(seller, escrow_id); + let vault = vault_pda(&escrow); + Instruction::new_with_bytes( + descro::id(), + &descro::instruction::Complete {}.data(), + descro::accounts::Complete { + buyer: *buyer, + seller: *seller, + escrow_account: escrow, + vault, + system_program: system_program::ID, + } + .to_account_metas(None), + ) +} + +pub fn ix_dispute(initiator: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction { + let escrow = escrow_pda(seller, escrow_id); + Instruction::new_with_bytes( + descro::id(), + &descro::instruction::Dispute {}.data(), + descro::accounts::Dispute { + initiator: *initiator, + escrow_account: escrow, + } + .to_account_metas(None), + ) +} + +pub fn ix_resolve( + resolver: &Pubkey, + winner_pubkey: &Pubkey, + seller: &Pubkey, + escrow_id: u64, + winner: Winner, +) -> Instruction { + let escrow = escrow_pda(seller, escrow_id); + let vault = vault_pda(&escrow); + Instruction::new_with_bytes( + descro::id(), + &descro::instruction::Resolve { winner }.data(), + descro::accounts::Resolve { + resolver: *resolver, + winner: *winner_pubkey, + seller: *seller, + escrow_account: escrow, + vault, + system_program: system_program::ID, + } + .to_account_metas(None), + ) +} + +pub fn ix_cancel(seller: &Pubkey, escrow_id: u64) -> Instruction { + let escrow = escrow_pda(seller, escrow_id); + Instruction::new_with_bytes( + descro::id(), + &descro::instruction::Cancel {}.data(), + descro::accounts::Cancel { + seller: *seller, + escrow_account: escrow, + system_program: system_program::ID, + } + .to_account_metas(None), + ) +} + +pub fn read_escrow(svm: &LiteSVM, seller: &Pubkey, escrow_id: u64) -> EscrowAccount { + let escrow = escrow_pda(seller, escrow_id); + let account = svm.get_account(&escrow).expect("escrow account not found"); + EscrowAccount::try_deserialize(&mut account.data.as_slice()).unwrap() +} diff --git a/programs/descro/tests/escrow_tests.rs b/programs/descro/tests/escrow_tests.rs deleted file mode 100644 index 03c9fc0..0000000 --- a/programs/descro/tests/escrow_tests.rs +++ /dev/null @@ -1,525 +0,0 @@ -use { - anchor_lang::{ - solana_program::{instruction::Instruction, system_program}, - AccountDeserialize, InstructionData, ToAccountMetas, - }, - descro::{EscrowAccount, EscrowState, Winner}, - litesvm::LiteSVM, - solana_keypair::Keypair, - solana_message::{Message, VersionedMessage}, - solana_signer::Signer, - solana_transaction::versioned::VersionedTransaction, -}; -use anchor_lang::prelude::Pubkey; - -const AMOUNT: u64 = 1_000_000_000; // 1 SOL -const ESCROW_ID: u64 = 1; - -// ── helpers ────────────────────────────────────────────────────────────────── - -fn setup() -> (LiteSVM, Keypair, Keypair, Keypair) { - let program_id = descro::id(); - let mut svm = LiteSVM::new(); - let bytes = include_bytes!("../../../target/deploy/descro.so"); - svm.add_program(program_id, bytes).unwrap(); - - let seller = Keypair::new(); - let buyer = Keypair::new(); - let resolver = Keypair::new(); - - svm.airdrop(&seller.pubkey(), 10_000_000_000).unwrap(); - svm.airdrop(&buyer.pubkey(), 10_000_000_000).unwrap(); - svm.airdrop(&resolver.pubkey(), 1_000_000_000).unwrap(); - - (svm, seller, buyer, resolver) -} - -fn escrow_pda(seller: &Pubkey, escrow_id: u64) -> Pubkey { - Pubkey::find_program_address( - &[b"escrow", seller.as_ref(), &escrow_id.to_le_bytes()], - &descro::id(), - ) - .0 -} - -fn vault_pda(escrow: &Pubkey) -> Pubkey { - Pubkey::find_program_address(&[b"vault", escrow.as_ref()], &descro::id()).0 -} - -fn send(svm: &mut LiteSVM, ix: Instruction, payer: &Keypair) { - let blockhash = svm.latest_blockhash(); - let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash); - let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[payer]).unwrap(); - svm.send_transaction(tx).expect("transaction failed"); -} - -fn try_send(svm: &mut LiteSVM, ix: Instruction, payer: &Keypair) -> bool { - let blockhash = svm.latest_blockhash(); - let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash); - let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[payer]).unwrap(); - svm.send_transaction(tx).is_ok() -} - -fn ix_create_escrow( - seller: &Pubkey, - buyer: &Pubkey, - amount: u64, - dispute_resolver: Option, - escrow_id: u64, -) -> Instruction { - let escrow = escrow_pda(seller, escrow_id); - let vault = vault_pda(&escrow); - Instruction::new_with_bytes( - descro::id(), - &descro::instruction::CreateEscrow { amount, dispute_resolver, escrow_id }.data(), - descro::accounts::CreateEscrow { - seller: *seller, - buyer: *buyer, - escrow_account: escrow, - vault, - system_program: system_program::ID, - } - .to_account_metas(None), - ) -} - -fn ix_deposit(buyer: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction { - let escrow = escrow_pda(seller, escrow_id); - let vault = vault_pda(&escrow); - Instruction::new_with_bytes( - descro::id(), - &descro::instruction::Deposit {}.data(), - descro::accounts::Deposit { - buyer: *buyer, - escrow_account: escrow, - vault, - system_program: system_program::ID, - } - .to_account_metas(None), - ) -} - -fn ix_complete(buyer: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction { - let escrow = escrow_pda(seller, escrow_id); - let vault = vault_pda(&escrow); - Instruction::new_with_bytes( - descro::id(), - &descro::instruction::Complete {}.data(), - descro::accounts::Complete { - buyer: *buyer, - seller: *seller, - escrow_account: escrow, - vault, - system_program: system_program::ID, - } - .to_account_metas(None), - ) -} - -fn ix_dispute(initiator: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction { - let escrow = escrow_pda(seller, escrow_id); - Instruction::new_with_bytes( - descro::id(), - &descro::instruction::Dispute {}.data(), - descro::accounts::Dispute { - initiator: *initiator, - escrow_account: escrow, - } - .to_account_metas(None), - ) -} - -fn ix_resolve( - resolver: &Pubkey, - winner_pubkey: &Pubkey, - seller: &Pubkey, - escrow_id: u64, - winner: Winner, -) -> Instruction { - let escrow = escrow_pda(seller, escrow_id); - let vault = vault_pda(&escrow); - Instruction::new_with_bytes( - descro::id(), - &descro::instruction::Resolve { winner }.data(), - descro::accounts::Resolve { - resolver: *resolver, - winner: *winner_pubkey, - seller: *seller, - escrow_account: escrow, - vault, - system_program: system_program::ID, - } - .to_account_metas(None), - ) -} - -fn ix_cancel(seller: &Pubkey, escrow_id: u64) -> Instruction { - let escrow = escrow_pda(seller, escrow_id); - Instruction::new_with_bytes( - descro::id(), - &descro::instruction::Cancel {}.data(), - descro::accounts::Cancel { - seller: *seller, - escrow_account: escrow, - system_program: system_program::ID, - } - .to_account_metas(None), - ) -} - -fn read_escrow(svm: &LiteSVM, seller: &Pubkey, escrow_id: u64) -> EscrowAccount { - let escrow = escrow_pda(seller, escrow_id); - let account = svm.get_account(&escrow).expect("escrow account not found"); - EscrowAccount::try_deserialize(&mut account.data.as_slice()).unwrap() -} - -// ── tests ───────────────────────────────────────────────────────────────────── - -#[test] -fn test_create_escrow() { - let (mut svm, seller, buyer, _resolver) = setup(); - - let ix = ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID); - send(&mut svm, ix, &seller); - - let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); - assert_eq!(escrow.seller, seller.pubkey()); - assert_eq!(escrow.buyer, buyer.pubkey()); - assert_eq!(escrow.amount, AMOUNT); - assert_eq!(escrow.state, EscrowState::AwaitingDeposit); - assert!(escrow.dispute_resolver.is_none()); -} - -#[test] -fn test_create_escrow_with_resolver() { - let (mut svm, seller, buyer, resolver) = setup(); - - let ix = ix_create_escrow( - &seller.pubkey(), - &buyer.pubkey(), - AMOUNT, - Some(resolver.pubkey()), - ESCROW_ID, - ); - send(&mut svm, ix, &seller); - - let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); - assert_eq!(escrow.dispute_resolver, Some(resolver.pubkey())); -} - -#[test] -fn test_deposit() { - let (mut svm, seller, buyer, _resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - let vault = vault_pda(&escrow_pda(&seller.pubkey(), ESCROW_ID)); - let vault_before = svm.get_account(&vault).map(|a| a.lamports).unwrap_or(0); - - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); - assert_eq!(escrow.state, EscrowState::Active); - - let vault_after = svm.get_account(&vault).map(|a| a.lamports).unwrap_or(0); - assert_eq!(vault_after - vault_before, AMOUNT); -} - -#[test] -fn test_complete_flow() { - let (mut svm, seller, buyer, _resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - let seller_before = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); - - send( - &mut svm, - ix_complete(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), - &buyer, - ); - - // Escrow account should be closed - assert!(svm.get_account(&escrow_pda(&seller.pubkey(), ESCROW_ID)).is_none()); - // Seller should have received the funds - let seller_after = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); - assert!(seller_after > seller_before); -} - -#[test] -fn test_dispute_by_buyer() { - let (mut svm, seller, buyer, _resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - send(&mut svm, ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); - assert_eq!(escrow.state, EscrowState::Disputed); -} - -#[test] -fn test_dispute_by_seller() { - let (mut svm, seller, buyer, _resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - send(&mut svm, ix_dispute(&seller.pubkey(), &seller.pubkey(), ESCROW_ID), &seller); - - let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); - assert_eq!(escrow.state, EscrowState::Disputed); -} - -#[test] -fn test_resolve_seller_wins() { - let (mut svm, seller, buyer, resolver) = setup(); - - send( - &mut svm, - ix_create_escrow( - &seller.pubkey(), - &buyer.pubkey(), - AMOUNT, - Some(resolver.pubkey()), - ESCROW_ID, - ), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - send(&mut svm, ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - let seller_before = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); - - send( - &mut svm, - ix_resolve( - &resolver.pubkey(), - &seller.pubkey(), - &seller.pubkey(), - ESCROW_ID, - Winner::Seller, - ), - &resolver, - ); - - assert!(svm.get_account(&escrow_pda(&seller.pubkey(), ESCROW_ID)).is_none()); - let seller_after = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); - assert!(seller_after > seller_before); -} - -#[test] -fn test_resolve_buyer_wins() { - let (mut svm, seller, buyer, resolver) = setup(); - - send( - &mut svm, - ix_create_escrow( - &seller.pubkey(), - &buyer.pubkey(), - AMOUNT, - Some(resolver.pubkey()), - ESCROW_ID, - ), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - send(&mut svm, ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - let buyer_before = svm.get_account(&buyer.pubkey()).map(|a| a.lamports).unwrap_or(0); - - send( - &mut svm, - ix_resolve( - &resolver.pubkey(), - &buyer.pubkey(), - &seller.pubkey(), - ESCROW_ID, - Winner::Buyer, - ), - &resolver, - ); - - let buyer_after = svm.get_account(&buyer.pubkey()).map(|a| a.lamports).unwrap_or(0); - assert!(buyer_after > buyer_before); -} - -#[test] -fn test_cancel() { - let (mut svm, seller, buyer, _resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - - let seller_before = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); - send(&mut svm, ix_cancel(&seller.pubkey(), ESCROW_ID), &seller); - - // Escrow account closed, rent returned - assert!(svm.get_account(&escrow_pda(&seller.pubkey(), ESCROW_ID)).is_none()); - let seller_after = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); - assert!(seller_after > seller_before); -} - -// ── error cases ─────────────────────────────────────────────────────────────── - -#[test] -fn test_deposit_wrong_buyer_fails() { - let (mut svm, seller, buyer, resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - - // resolver is NOT the buyer — should fail - let bad_ix = ix_deposit(&resolver.pubkey(), &seller.pubkey(), ESCROW_ID); - assert!(!try_send(&mut svm, bad_ix, &resolver)); -} - -#[test] -fn test_complete_wrong_state_fails() { - let (mut svm, seller, buyer, _resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - // Not deposited yet — state is AwaitingDeposit, not Active - let bad_ix = ix_complete(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID); - assert!(!try_send(&mut svm, bad_ix, &buyer)); -} - -#[test] -fn test_complete_wrong_buyer_fails() { - let (mut svm, seller, buyer, resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - // resolver is not the buyer - let bad_ix = ix_complete(&resolver.pubkey(), &seller.pubkey(), ESCROW_ID); - assert!(!try_send(&mut svm, bad_ix, &resolver)); -} - -#[test] -fn test_resolve_no_resolver_fails() { - let (mut svm, seller, buyer, resolver) = setup(); - - // Create escrow WITHOUT a resolver - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - send(&mut svm, ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - let bad_ix = ix_resolve( - &resolver.pubkey(), - &seller.pubkey(), - &seller.pubkey(), - ESCROW_ID, - Winner::Seller, - ); - assert!(!try_send(&mut svm, bad_ix, &resolver)); -} - -#[test] -fn test_resolve_wrong_resolver_fails() { - let (mut svm, seller, buyer, resolver) = setup(); - let wrong_resolver = Keypair::new(); - svm.airdrop(&wrong_resolver.pubkey(), 1_000_000_000).unwrap(); - - send( - &mut svm, - ix_create_escrow( - &seller.pubkey(), - &buyer.pubkey(), - AMOUNT, - Some(resolver.pubkey()), - ESCROW_ID, - ), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - send(&mut svm, ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - let bad_ix = ix_resolve( - &wrong_resolver.pubkey(), - &seller.pubkey(), - &seller.pubkey(), - ESCROW_ID, - Winner::Seller, - ); - assert!(!try_send(&mut svm, bad_ix, &wrong_resolver)); -} - -#[test] -fn test_cancel_after_deposit_fails() { - let (mut svm, seller, buyer, _resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - // State is now Active, cancel requires AwaitingDeposit - let bad_ix = ix_cancel(&seller.pubkey(), ESCROW_ID); - assert!(!try_send(&mut svm, bad_ix, &seller)); -} - -#[test] -fn test_dispute_wrong_state_fails() { - let (mut svm, seller, buyer, _resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - // No deposit — state is AwaitingDeposit, dispute requires Active - let bad_ix = ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID); - assert!(!try_send(&mut svm, bad_ix, &buyer)); -} - -#[test] -fn test_dispute_third_party_fails() { - let (mut svm, seller, buyer, resolver) = setup(); - - send( - &mut svm, - ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), - &seller, - ); - send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); - - // Resolver is neither buyer nor seller - let bad_ix = ix_dispute(&resolver.pubkey(), &seller.pubkey(), ESCROW_ID); - assert!(!try_send(&mut svm, bad_ix, &resolver)); -} diff --git a/programs/descro/tests/test_cancel.rs b/programs/descro/tests/test_cancel.rs new file mode 100644 index 0000000..9f5742a --- /dev/null +++ b/programs/descro/tests/test_cancel.rs @@ -0,0 +1,40 @@ +mod common; +use common::*; +use solana_signer::Signer; + +#[test] +fn cancel_returns_rent_to_seller() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + + let seller_before = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); + + send(&mut svm, ix_cancel(&seller.pubkey(), ESCROW_ID), &seller); + + assert!(svm.get_account(&escrow_pda(&seller.pubkey(), ESCROW_ID)).is_none()); + let seller_after = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); + assert!(seller_after > seller_before); +} + +#[test] +fn cancel_after_deposit_fails() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + + assert!(!try_send( + &mut svm, + ix_cancel(&seller.pubkey(), ESCROW_ID), + &seller, + )); +} diff --git a/programs/descro/tests/test_complete.rs b/programs/descro/tests/test_complete.rs new file mode 100644 index 0000000..6cab93d --- /dev/null +++ b/programs/descro/tests/test_complete.rs @@ -0,0 +1,62 @@ +mod common; +use common::*; +use solana_signer::Signer; + +#[test] +fn complete_releases_funds_to_seller() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + + let seller_before = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); + + send( + &mut svm, + ix_complete(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), + &buyer, + ); + + assert!(svm.get_account(&escrow_pda(&seller.pubkey(), ESCROW_ID)).is_none()); + let seller_after = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); + assert!(seller_after > seller_before); +} + +#[test] +fn complete_before_deposit_fails() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + + assert!(!try_send( + &mut svm, + ix_complete(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), + &buyer, + )); +} + +#[test] +fn complete_wrong_buyer_fails() { + let (mut svm, seller, buyer, resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + + assert!(!try_send( + &mut svm, + ix_complete(&resolver.pubkey(), &seller.pubkey(), ESCROW_ID), + &resolver, + )); +} diff --git a/programs/descro/tests/test_create_escrow.rs b/programs/descro/tests/test_create_escrow.rs new file mode 100644 index 0000000..3afc94c --- /dev/null +++ b/programs/descro/tests/test_create_escrow.rs @@ -0,0 +1,63 @@ +mod common; +use common::*; +use solana_signer::Signer; + +#[test] +fn creates_escrow_with_correct_state() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + + let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); + assert_eq!(escrow.seller, seller.pubkey()); + assert_eq!(escrow.buyer, buyer.pubkey()); + assert_eq!(escrow.amount, AMOUNT); + assert_eq!(escrow.state, EscrowState::AwaitingDeposit); + assert!(escrow.dispute_resolver.is_none()); +} + +#[test] +fn creates_escrow_with_resolver() { + let (mut svm, seller, buyer, resolver) = setup(); + + send( + &mut svm, + ix_create_escrow( + &seller.pubkey(), + &buyer.pubkey(), + AMOUNT, + Some(resolver.pubkey()), + ESCROW_ID, + ), + &seller, + ); + + let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); + assert_eq!(escrow.dispute_resolver, Some(resolver.pubkey())); +} + +#[test] +fn different_escrow_ids_produce_distinct_pdas() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, 1), + &seller, + ); + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, 2), + &seller, + ); + + let escrow1 = read_escrow(&svm, &seller.pubkey(), 1); + let escrow2 = read_escrow(&svm, &seller.pubkey(), 2); + assert_eq!(escrow1.escrow_id, 1); + assert_eq!(escrow2.escrow_id, 2); + assert_ne!(escrow_pda(&seller.pubkey(), 1), escrow_pda(&seller.pubkey(), 2)); +} diff --git a/programs/descro/tests/test_deposit.rs b/programs/descro/tests/test_deposit.rs new file mode 100644 index 0000000..6f62231 --- /dev/null +++ b/programs/descro/tests/test_deposit.rs @@ -0,0 +1,59 @@ +mod common; +use common::*; +use solana_signer::Signer; + +#[test] +fn deposit_transfers_funds_and_activates() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + let vault = vault_pda(&escrow_pda(&seller.pubkey(), ESCROW_ID)); + let vault_before = svm.get_account(&vault).map(|a| a.lamports).unwrap_or(0); + + send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + + let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); + assert_eq!(escrow.state, EscrowState::Active); + let vault_after = svm.get_account(&vault).map(|a| a.lamports).unwrap_or(0); + assert_eq!(vault_after - vault_before, AMOUNT); +} + +#[test] +fn deposit_wrong_buyer_fails() { + let (mut svm, seller, buyer, resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + + assert!(!try_send( + &mut svm, + ix_deposit(&resolver.pubkey(), &seller.pubkey(), ESCROW_ID), + &resolver, + )); +} + +#[test] +fn double_deposit_fails() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + + // State is now Active, not AwaitingDeposit + assert!(!try_send( + &mut svm, + ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), + &buyer, + )); +} diff --git a/programs/descro/tests/test_dispute.rs b/programs/descro/tests/test_dispute.rs new file mode 100644 index 0000000..1179fa3 --- /dev/null +++ b/programs/descro/tests/test_dispute.rs @@ -0,0 +1,64 @@ +mod common; +use common::*; +use solana_signer::Signer; + +fn setup_active(svm: &mut litesvm::LiteSVM, seller: &solana_keypair::Keypair, buyer: &solana_keypair::Keypair) { + use solana_signer::Signer; + send( + svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + seller, + ); + send(svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), buyer); +} + +#[test] +fn buyer_can_dispute() { + let (mut svm, seller, buyer, _resolver) = setup(); + setup_active(&mut svm, &seller, &buyer); + + send(&mut svm, ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + + let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); + assert_eq!(escrow.state, EscrowState::Disputed); +} + +#[test] +fn seller_can_dispute() { + let (mut svm, seller, buyer, _resolver) = setup(); + setup_active(&mut svm, &seller, &buyer); + + send(&mut svm, ix_dispute(&seller.pubkey(), &seller.pubkey(), ESCROW_ID), &seller); + + let escrow = read_escrow(&svm, &seller.pubkey(), ESCROW_ID); + assert_eq!(escrow.state, EscrowState::Disputed); +} + +#[test] +fn dispute_before_deposit_fails() { + let (mut svm, seller, buyer, _resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + + assert!(!try_send( + &mut svm, + ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), + &buyer, + )); +} + +#[test] +fn third_party_cannot_dispute() { + let (mut svm, seller, buyer, resolver) = setup(); + setup_active(&mut svm, &seller, &buyer); + + assert!(!try_send( + &mut svm, + ix_dispute(&resolver.pubkey(), &seller.pubkey(), ESCROW_ID), + &resolver, + )); +} diff --git a/programs/descro/tests/test_resolve.rs b/programs/descro/tests/test_resolve.rs new file mode 100644 index 0000000..295a07c --- /dev/null +++ b/programs/descro/tests/test_resolve.rs @@ -0,0 +1,118 @@ +mod common; +use common::*; +use solana_keypair::Keypair; +use solana_signer::Signer; + +fn setup_disputed( + svm: &mut litesvm::LiteSVM, + seller: &Keypair, + buyer: &Keypair, + resolver: &Keypair, +) { + send( + svm, + ix_create_escrow( + &seller.pubkey(), + &buyer.pubkey(), + AMOUNT, + Some(resolver.pubkey()), + ESCROW_ID, + ), + seller, + ); + send(svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), buyer); + send(svm, ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), buyer); +} + +#[test] +fn resolver_can_rule_for_seller() { + let (mut svm, seller, buyer, resolver) = setup(); + setup_disputed(&mut svm, &seller, &buyer, &resolver); + + let seller_before = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); + + send( + &mut svm, + ix_resolve(&resolver.pubkey(), &seller.pubkey(), &seller.pubkey(), ESCROW_ID, EscrowWinner::Seller), + &resolver, + ); + + assert!(svm.get_account(&escrow_pda(&seller.pubkey(), ESCROW_ID)).is_none()); + let seller_after = svm.get_account(&seller.pubkey()).map(|a| a.lamports).unwrap_or(0); + assert!(seller_after > seller_before); +} + +#[test] +fn resolver_can_rule_for_buyer() { + let (mut svm, seller, buyer, resolver) = setup(); + setup_disputed(&mut svm, &seller, &buyer, &resolver); + + let buyer_before = svm.get_account(&buyer.pubkey()).map(|a| a.lamports).unwrap_or(0); + + send( + &mut svm, + ix_resolve(&resolver.pubkey(), &buyer.pubkey(), &seller.pubkey(), ESCROW_ID, EscrowWinner::Buyer), + &resolver, + ); + + let buyer_after = svm.get_account(&buyer.pubkey()).map(|a| a.lamports).unwrap_or(0); + assert!(buyer_after > buyer_before); +} + +#[test] +fn resolve_without_configured_resolver_fails() { + let (mut svm, seller, buyer, resolver) = setup(); + + send( + &mut svm, + ix_create_escrow(&seller.pubkey(), &buyer.pubkey(), AMOUNT, None, ESCROW_ID), + &seller, + ); + send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + send(&mut svm, ix_dispute(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + + assert!(!try_send( + &mut svm, + ix_resolve(&resolver.pubkey(), &seller.pubkey(), &seller.pubkey(), ESCROW_ID, EscrowWinner::Seller), + &resolver, + )); +} + +#[test] +fn wrong_resolver_fails() { + let (mut svm, seller, buyer, resolver) = setup(); + let wrong = Keypair::new(); + svm.airdrop(&wrong.pubkey(), 1_000_000_000).unwrap(); + setup_disputed(&mut svm, &seller, &buyer, &resolver); + + assert!(!try_send( + &mut svm, + ix_resolve(&wrong.pubkey(), &seller.pubkey(), &seller.pubkey(), ESCROW_ID, EscrowWinner::Seller), + &wrong, + )); +} + +#[test] +fn resolve_before_dispute_fails() { + let (mut svm, seller, buyer, resolver) = setup(); + + send( + &mut svm, + ix_create_escrow( + &seller.pubkey(), + &buyer.pubkey(), + AMOUNT, + Some(resolver.pubkey()), + ESCROW_ID, + ), + &seller, + ); + send(&mut svm, ix_deposit(&buyer.pubkey(), &seller.pubkey(), ESCROW_ID), &buyer); + + // State is Active, not Disputed + assert!(!try_send( + &mut svm, + ix_resolve(&resolver.pubkey(), &seller.pubkey(), &seller.pubkey(), ESCROW_ID, EscrowWinner::Seller), + &resolver, + )); +}