light_program_test/
litesvm_extensions.rs

1use litesvm::LiteSVM;
2use solana_account::ReadableAccount;
3use solana_sdk::{account::Account, pubkey::Pubkey};
4
5/// Extension trait for LiteSVM to add utility methods.
6///
7/// This trait provides additional functionality on top of the base LiteSVM,
8/// such as querying all accounts owned by a specific program.
9pub trait LiteSvmExtensions {
10    /// Returns all accounts owned by the provided program id.
11    ///
12    /// This method iterates through the internal accounts database and filters
13    /// accounts by their owner field.
14    ///
15    /// # Arguments
16    ///
17    /// * `program_id` - The program ID to filter accounts by
18    ///
19    /// # Returns
20    ///
21    /// A vector of tuples containing (Pubkey, Account) for all accounts owned by the program
22    fn get_program_accounts(&self, program_id: &Pubkey) -> Vec<(Pubkey, Account)>;
23}
24
25impl LiteSvmExtensions for LiteSVM {
26    fn get_program_accounts(&self, program_id: &Pubkey) -> Vec<(Pubkey, Account)> {
27        self.accounts_db()
28            .inner
29            .iter()
30            .filter(|(_, account)| account.owner() == program_id)
31            .map(|(pubkey, account)| (*pubkey, Account::from(account.clone())))
32            .collect()
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use solana_sdk::{
39        native_token::LAMPORTS_PER_SOL,
40        signature::{Keypair, Signer},
41        system_instruction::{create_account, transfer},
42        system_program,
43        transaction::{Transaction, VersionedTransaction},
44    };
45
46    use super::*;
47
48    #[test]
49    fn test_get_program_accounts() {
50        let mut svm = LiteSVM::new();
51        let payer_keypair = Keypair::new();
52        let payer = payer_keypair.pubkey();
53
54        // Fund the payer
55        svm.airdrop(&payer, 10 * LAMPORTS_PER_SOL).unwrap();
56
57        // Establish baseline of system accounts
58        let baseline_system_accounts = svm.get_program_accounts(&system_program::id());
59        let baseline_count = baseline_system_accounts.len();
60
61        // Create multiple accounts owned by system program
62        let num_system_accounts = 5;
63        let mut system_owned_accounts = vec![];
64
65        for i in 0..num_system_accounts {
66            let new_account_keypair = Keypair::new();
67            let new_account = new_account_keypair.pubkey();
68            let space = 10 + i;
69            let rent_amount = svm.minimum_balance_for_rent_exemption(space);
70
71            let instruction = create_account(
72                &payer,
73                &new_account,
74                rent_amount,
75                space as u64,
76                &system_program::id(),
77            );
78
79            let tx = Transaction::new_signed_with_payer(
80                &[instruction],
81                Some(&payer),
82                &[&payer_keypair, &new_account_keypair],
83                svm.latest_blockhash(),
84            );
85
86            svm.send_transaction(VersionedTransaction::from(tx))
87                .unwrap();
88            system_owned_accounts.push((new_account, rent_amount, space));
89        }
90
91        // Create a custom program and some accounts owned by it
92        let custom_program_id = Pubkey::new_unique();
93        let num_custom_accounts = 3;
94        let mut custom_owned_accounts = vec![];
95
96        for i in 0..num_custom_accounts {
97            let new_account_keypair = Keypair::new();
98            let new_account = new_account_keypair.pubkey();
99            let space = 20 + i * 2;
100            let rent_amount = svm.minimum_balance_for_rent_exemption(space);
101
102            let instruction = create_account(
103                &payer,
104                &new_account,
105                rent_amount,
106                space as u64,
107                &custom_program_id,
108            );
109
110            let tx = Transaction::new_signed_with_payer(
111                &[instruction],
112                Some(&payer),
113                &[&payer_keypair, &new_account_keypair],
114                svm.latest_blockhash(),
115            );
116
117            svm.send_transaction(VersionedTransaction::from(tx))
118                .unwrap();
119            custom_owned_accounts.push((new_account, rent_amount, space));
120        }
121
122        // Do some transfers to create new accounts
123        for i in 0..3 {
124            let to = Pubkey::new_unique();
125            let amount = (i + 1) * 1000;
126            let instruction = transfer(&payer, &to, amount);
127            let tx = Transaction::new_signed_with_payer(
128                &[instruction],
129                Some(&payer),
130                &[&payer_keypair],
131                svm.latest_blockhash(),
132            );
133            svm.send_transaction(VersionedTransaction::from(tx))
134                .unwrap();
135        }
136
137        // Test get_program_accounts for system program
138        let system_accounts = svm.get_program_accounts(&system_program::id());
139
140        // Should contain baseline + 5 created accounts + 3 transfer recipients
141        let expected_count = baseline_count + num_system_accounts + 3;
142        assert_eq!(
143            system_accounts.len(),
144            expected_count,
145            "Expected {} system accounts (baseline {} + {} created + 3 transfers), got {}",
146            expected_count,
147            baseline_count,
148            num_system_accounts,
149            system_accounts.len()
150        );
151
152        // Verify all our created system accounts are present with correct data
153        for (pubkey, expected_lamports, expected_space) in &system_owned_accounts {
154            let found = system_accounts
155                .iter()
156                .find(|(pk, _)| pk == pubkey)
157                .expect("System account should be found");
158
159            assert_eq!(found.1.lamports, *expected_lamports);
160            assert_eq!(found.1.data.len(), *expected_space);
161            assert_eq!(found.1.owner, system_program::id());
162        }
163
164        // Verify individual get_account returns the same data
165        for (pubkey, _, _) in &system_owned_accounts {
166            let individual_account = svm.get_account(pubkey).unwrap();
167            let from_program_accounts =
168                system_accounts.iter().find(|(pk, _)| pk == pubkey).unwrap();
169
170            assert_eq!(
171                individual_account.lamports,
172                from_program_accounts.1.lamports
173            );
174            assert_eq!(individual_account.data, from_program_accounts.1.data);
175            assert_eq!(individual_account.owner, from_program_accounts.1.owner);
176        }
177
178        // Test get_program_accounts for custom program
179        let custom_accounts = svm.get_program_accounts(&custom_program_id);
180        assert_eq!(custom_accounts.len(), num_custom_accounts);
181
182        // Verify all custom accounts are present with correct data
183        for (pubkey, expected_lamports, expected_space) in &custom_owned_accounts {
184            let found = custom_accounts
185                .iter()
186                .find(|(pk, _)| pk == pubkey)
187                .expect("Custom account should be found");
188
189            assert_eq!(found.1.lamports, *expected_lamports);
190            assert_eq!(found.1.data.len(), *expected_space);
191            assert_eq!(found.1.owner, custom_program_id);
192        }
193
194        // Test get_program_accounts for non-existent program
195        let nonexistent_program = Pubkey::new_unique();
196        let no_accounts = svm.get_program_accounts(&nonexistent_program);
197        assert_eq!(no_accounts.len(), 0);
198    }
199}