//! `multisig_list::multisig_list::close` instruction tests. use solana_sdk::account::Account; use solana_sdk::commitment_config::CommitmentLevel; use solana_sdk::hash::Hash; use solana_sdk::instruction::AccountMeta; use solana_sdk::pubkey::Pubkey; use solana_sdk::signer::keypair::Keypair; use solana_sdk::signer::Signer; use solana_sdk::system_program; use solana_sdk::transaction::{Transaction, TransactionError}; use anchor_client::anchor_lang::AccountDeserialize; #[tokio::test] async fn close() { let mut tester = Tester::new().await; // Creates a multisig account. tester.create().await; // Make sure state and fund accounts exist. assert!(tester.get_state_account().await.is_some()); assert!(tester.get_fund_account().await.is_some()); // Then close it. assert!(tester.with_signature().close().await.is_ok()); // And double check if it's gone. assert!(tester.get_state_account().await.is_none()); assert!(tester.get_fund_account().await.is_none()); } #[tokio::test] async fn close_without_signature() { let mut tester = Tester::new().await; // Creates a multisig account. tester.create().await; let err = tester.close().await.err().unwrap(); assert_eq!(err.unwrap(), TransactionError::SignatureFailure); } struct Tester { program: anchor_client::Program, client: solana_program_test::BanksClient, funder: std::rc::Rc, recent_blockhash: Hash, with_signature: bool, m: u8, signers: Vec, q: u8, state_pda: Pubkey, state_bump: u8, fund_pda: Pubkey, fund_bump: u8, } impl Tester { async fn new() -> Self { let pid = multisig_lite::id(); let (client, funder, recent_blockhash) = solana_program_test::ProgramTest::new( "multisig_lite", pid, solana_program_test::processor!(multisig_lite::entry), ) .start() .await; // Wrap the founder keypair to be able to be passed to the anchor program. let funder = std::rc::Rc::new(funder); // Default 5 signers, including the funder. let signers = vec![ funder.pubkey(), Pubkey::new_unique(), Pubkey::new_unique(), Pubkey::new_unique(), Pubkey::new_unique(), ]; // Creates an anchor::Program for `solana_sdk::instruction::Instruction`s. let cluster = anchor_client::Cluster::Localnet; let program = anchor_client::Client::new(cluster, funder.clone()).program(pid); // Find PDAs. let (state_pda, state_bump) = Pubkey::find_program_address(&[b"state", (funder.pubkey().as_ref())], &pid); let (fund_pda, fund_bump) = Pubkey::find_program_address(&[b"fund", state_pda.as_ref()], &pid); Self { program, client, funder, recent_blockhash, with_signature: false, m: 3, signers, q: 10, state_pda, state_bump, fund_pda, fund_bump, } } fn with_signature(&mut self) -> &mut Self { self.with_signature = true; self } async fn get_state_account(&mut self) -> Option { self.client .get_account_with_commitment(self.state_pda, CommitmentLevel::Processed) .await .unwrap() .map(|account| { let mut data: &[u8] = &account.data; multisig_lite::State::try_deserialize(&mut data).unwrap() }) } async fn get_fund_account(&mut self) -> Option { self.client.get_account(self.fund_pda).await.unwrap() } async fn create(&mut self) { let ixs = self .program .request() .accounts(multisig_lite::accounts::Create { funder: self.funder.pubkey(), state: self.state_pda, fund: self.fund_pda, system_program: system_program::id(), }) .args(multisig_lite::instruction::Create { m: self.m, signers: self.signers.clone(), q: self.q, _state_bump: self.state_bump, fund_bump: self.fund_bump, }) .instructions() .unwrap(); let mut tx = Transaction::new_with_payer(&ixs, Some(&self.funder.pubkey())); tx.sign(&[self.funder.as_ref()], self.recent_blockhash); self.client.process_transaction(tx).await.unwrap(); } async fn close(&mut self) -> Result<(), solana_program_test::BanksClientError> { // Gets the remaining transfers to collects the rents. let state = self.get_state_account().await.unwrap(); let remaining_accounts: Vec<_> = state .queue .into_iter() .map(|pubkey| AccountMeta { pubkey, is_signer: false, is_writable: true, }) .collect(); let ixs = self .program .request() .accounts(multisig_lite::accounts::Close { funder: self.funder.pubkey(), state: self.state_pda, fund: self.fund_pda, }) .args(multisig_lite::instruction::Close { _state_bump: self.state_bump, fund_bump: self.fund_bump, }) .accounts(remaining_accounts) .instructions() .unwrap(); let mut tx = Transaction::new_with_payer(&ixs, Some(&self.funder.pubkey())); if self.with_signature { tx.sign(&[self.funder.as_ref()], self.recent_blockhash); } self.client.process_transaction(tx).await } }