Skip to main content

litesvm_utils/
assertions.rs

1//! Assertion helpers for testing account states
2//!
3//! This module provides convenient assertion methods for verifying
4//! account states in tests.
5
6use litesvm::LiteSVM;
7use litesvm_token::spl_token;
8use solana_program::pubkey::Pubkey;
9use solana_program_pack::Pack;
10
11/// Assertion helper methods for LiteSVM
12pub trait AssertionHelpers {
13    /// Assert that an account is closed (doesn't exist or has 0 lamports and 0 data)
14    ///
15    /// # Example
16    /// ```no_run
17    /// # use litesvm_utils::AssertionHelpers;
18    /// # use litesvm::LiteSVM;
19    /// # use solana_program::pubkey::Pubkey;
20    /// # let svm = LiteSVM::new();
21    /// # let account = Pubkey::new_unique();
22    /// svm.assert_account_closed(&account);
23    /// ```
24    fn assert_account_closed(&self, pubkey: &Pubkey);
25
26    /// Assert that an account exists
27    ///
28    /// # Example
29    /// ```no_run
30    /// # use litesvm_utils::AssertionHelpers;
31    /// # use litesvm::LiteSVM;
32    /// # use solana_program::pubkey::Pubkey;
33    /// # let svm = LiteSVM::new();
34    /// # let account = Pubkey::new_unique();
35    /// svm.assert_account_exists(&account);
36    /// ```
37    fn assert_account_exists(&self, pubkey: &Pubkey);
38
39    /// Assert token account balance
40    ///
41    /// # Example
42    /// ```no_run
43    /// # use litesvm_utils::AssertionHelpers;
44    /// # use litesvm::LiteSVM;
45    /// # use solana_program::pubkey::Pubkey;
46    /// # let svm = LiteSVM::new();
47    /// # let token_account = Pubkey::new_unique();
48    /// svm.assert_token_balance(&token_account, 1_000_000_000); // 1 token with 9 decimals
49    /// ```
50    fn assert_token_balance(&self, token_account: &Pubkey, expected: u64);
51
52    /// Assert SOL balance
53    ///
54    /// # Example
55    /// ```no_run
56    /// # use litesvm_utils::AssertionHelpers;
57    /// # use litesvm::LiteSVM;
58    /// # use solana_program::pubkey::Pubkey;
59    /// # let svm = LiteSVM::new();
60    /// # let account = Pubkey::new_unique();
61    /// svm.assert_sol_balance(&account, 1_000_000_000); // 1 SOL
62    /// ```
63    fn assert_sol_balance(&self, pubkey: &Pubkey, expected: u64);
64
65    /// Assert token mint supply
66    ///
67    /// # Example
68    /// ```no_run
69    /// # use litesvm_utils::AssertionHelpers;
70    /// # use litesvm::LiteSVM;
71    /// # use solana_program::pubkey::Pubkey;
72    /// # let svm = LiteSVM::new();
73    /// # let mint = Pubkey::new_unique();
74    /// svm.assert_mint_supply(&mint, 1_000_000_000);
75    /// ```
76    fn assert_mint_supply(&self, mint: &Pubkey, expected: u64);
77
78    /// Assert that an account is owned by a specific program
79    ///
80    /// # Example
81    /// ```no_run
82    /// # use litesvm_utils::AssertionHelpers;
83    /// # use litesvm::LiteSVM;
84    /// # use solana_program::pubkey::Pubkey;
85    /// # let svm = LiteSVM::new();
86    /// # let account = Pubkey::new_unique();
87    /// # let owner = Pubkey::new_unique();
88    /// svm.assert_account_owner(&account, &owner);
89    /// ```
90    fn assert_account_owner(&self, account: &Pubkey, expected_owner: &Pubkey);
91
92    /// Assert that an account has a specific data length
93    ///
94    /// # Example
95    /// ```no_run
96    /// # use litesvm_utils::AssertionHelpers;
97    /// # use litesvm::LiteSVM;
98    /// # use solana_program::pubkey::Pubkey;
99    /// # let svm = LiteSVM::new();
100    /// # let account = Pubkey::new_unique();
101    /// svm.assert_account_data_len(&account, 100);
102    /// ```
103    fn assert_account_data_len(&self, account: &Pubkey, expected_len: usize);
104}
105
106impl AssertionHelpers for LiteSVM {
107    fn assert_account_closed(&self, pubkey: &Pubkey) {
108        let account = self.get_account(pubkey);
109        assert!(
110            account.is_none()
111                || (account.as_ref().unwrap().lamports == 0
112                    && account.as_ref().unwrap().data.is_empty()),
113            "Expected account {} to be closed, but it exists with {} lamports and {} bytes of data",
114            pubkey,
115            account.as_ref().map_or(0, |a| a.lamports),
116            account.as_ref().map_or(0, |a| a.data.len())
117        );
118    }
119
120    fn assert_account_exists(&self, pubkey: &Pubkey) {
121        let account = self.get_account(pubkey);
122        assert!(
123            account.is_some(),
124            "Expected account {} to exist, but it doesn't",
125            pubkey
126        );
127    }
128
129    fn assert_token_balance(&self, token_account: &Pubkey, expected: u64) {
130        let account = self
131            .get_account(token_account)
132            .unwrap_or_else(|| panic!("Token account {} not found", token_account));
133
134        let token_data = spl_token::state::Account::unpack(&account.data)
135            .unwrap_or_else(|_| panic!("Failed to unpack token account {}", token_account));
136
137        assert_eq!(
138            token_data.amount, expected,
139            "Token balance mismatch for account {}. Expected: {}, Actual: {}",
140            token_account, expected, token_data.amount
141        );
142    }
143
144    fn assert_sol_balance(&self, pubkey: &Pubkey, expected: u64) {
145        let account = self.get_account(pubkey);
146        let actual = account.map_or(0, |a| a.lamports);
147        assert_eq!(
148            actual, expected,
149            "SOL balance mismatch for account {}. Expected: {}, Actual: {}",
150            pubkey, expected, actual
151        );
152    }
153
154    fn assert_mint_supply(&self, mint: &Pubkey, expected: u64) {
155        let account = self
156            .get_account(mint)
157            .unwrap_or_else(|| panic!("Mint {} not found", mint));
158
159        let mint_data = spl_token::state::Mint::unpack(&account.data)
160            .unwrap_or_else(|_| panic!("Failed to unpack mint {}", mint));
161
162        assert_eq!(
163            mint_data.supply, expected,
164            "Mint supply mismatch for {}. Expected: {}, Actual: {}",
165            mint, expected, mint_data.supply
166        );
167    }
168
169    fn assert_account_owner(&self, account: &Pubkey, expected_owner: &Pubkey) {
170        let acc = self
171            .get_account(account)
172            .unwrap_or_else(|| panic!("Account {} not found", account));
173
174        assert_eq!(
175            &acc.owner, expected_owner,
176            "Account owner mismatch for {}. Expected: {}, Actual: {}",
177            account, expected_owner, acc.owner
178        );
179    }
180
181    fn assert_account_data_len(&self, account: &Pubkey, expected_len: usize) {
182        let acc = self
183            .get_account(account)
184            .unwrap_or_else(|| panic!("Account {} not found", account));
185
186        assert_eq!(
187            acc.data.len(),
188            expected_len,
189            "Account data length mismatch for {}. Expected: {}, Actual: {}",
190            account,
191            expected_len,
192            acc.data.len()
193        );
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200    use crate::test_helpers::TestHelpers;
201    use solana_signer::Signer;
202
203    #[test]
204    fn test_assert_account_closed_nonexistent() {
205        let svm = LiteSVM::new();
206        let nonexistent_account = Pubkey::new_unique();
207
208        // Should not panic for non-existent account
209        svm.assert_account_closed(&nonexistent_account);
210    }
211
212    #[test]
213    fn test_assert_account_exists() {
214        let mut svm = LiteSVM::new();
215        let account = svm.create_funded_account(1_000_000_000).unwrap();
216
217        // Should not panic for existing account
218        svm.assert_account_exists(&account.pubkey());
219    }
220
221    #[test]
222    #[should_panic(expected = "Expected account")]
223    fn test_assert_account_exists_fails() {
224        let svm = LiteSVM::new();
225        let nonexistent = Pubkey::new_unique();
226
227        // Should panic for non-existent account
228        svm.assert_account_exists(&nonexistent);
229    }
230
231    #[test]
232    fn test_assert_token_balance() {
233        let mut svm = LiteSVM::new();
234        let authority = svm.create_funded_account(10_000_000_000).unwrap();
235        let mint = svm.create_token_mint(&authority, 9).unwrap();
236        let token_account = svm
237            .create_associated_token_account(&mint.pubkey(), &authority)
238            .unwrap();
239
240        // Mint tokens
241        let amount = 1_000_000;
242        svm.mint_to(&mint.pubkey(), &token_account, &authority, amount)
243            .unwrap();
244
245        // Should not panic with correct balance
246        svm.assert_token_balance(&token_account, amount);
247    }
248
249    #[test]
250    #[should_panic(expected = "Token balance mismatch")]
251    fn test_assert_token_balance_fails() {
252        let mut svm = LiteSVM::new();
253        let authority = svm.create_funded_account(10_000_000_000).unwrap();
254        let mint = svm.create_token_mint(&authority, 9).unwrap();
255        let token_account = svm
256            .create_associated_token_account(&mint.pubkey(), &authority)
257            .unwrap();
258
259        // Mint 1000 tokens
260        svm.mint_to(&mint.pubkey(), &token_account, &authority, 1000)
261            .unwrap();
262
263        // Should panic when expecting wrong balance
264        svm.assert_token_balance(&token_account, 2000);
265    }
266
267    #[test]
268    fn test_assert_sol_balance() {
269        let mut svm = LiteSVM::new();
270        let lamports = 5_000_000_000;
271        let account = svm.create_funded_account(lamports).unwrap();
272
273        // Should not panic with correct balance
274        svm.assert_sol_balance(&account.pubkey(), lamports);
275    }
276
277    #[test]
278    #[should_panic(expected = "SOL balance mismatch")]
279    fn test_assert_sol_balance_fails() {
280        let mut svm = LiteSVM::new();
281        let account = svm.create_funded_account(1_000_000_000).unwrap();
282
283        // Should panic when expecting wrong balance
284        svm.assert_sol_balance(&account.pubkey(), 2_000_000_000);
285    }
286
287    #[test]
288    fn test_assert_sol_balance_zero_for_nonexistent() {
289        let svm = LiteSVM::new();
290        let nonexistent = Pubkey::new_unique();
291
292        // Should not panic when expecting 0 for non-existent account
293        svm.assert_sol_balance(&nonexistent, 0);
294    }
295
296    #[test]
297    fn test_assert_mint_supply() {
298        let mut svm = LiteSVM::new();
299        let authority = svm.create_funded_account(10_000_000_000).unwrap();
300        let mint = svm.create_token_mint(&authority, 9).unwrap();
301        let token_account = svm
302            .create_associated_token_account(&mint.pubkey(), &authority)
303            .unwrap();
304
305        // Mint tokens
306        let amount = 5_000_000;
307        svm.mint_to(&mint.pubkey(), &token_account, &authority, amount)
308            .unwrap();
309
310        // Should not panic with correct supply
311        svm.assert_mint_supply(&mint.pubkey(), amount);
312    }
313
314    #[test]
315    #[should_panic(expected = "Mint supply mismatch")]
316    fn test_assert_mint_supply_fails() {
317        let mut svm = LiteSVM::new();
318        let authority = svm.create_funded_account(10_000_000_000).unwrap();
319        let mint = svm.create_token_mint(&authority, 9).unwrap();
320        let token_account = svm
321            .create_associated_token_account(&mint.pubkey(), &authority)
322            .unwrap();
323
324        // Mint 100 tokens
325        svm.mint_to(&mint.pubkey(), &token_account, &authority, 100)
326            .unwrap();
327
328        // Should panic when expecting wrong supply
329        svm.assert_mint_supply(&mint.pubkey(), 200);
330    }
331
332    #[test]
333    fn test_assert_mint_supply_zero_for_new_mint() {
334        let mut svm = LiteSVM::new();
335        let authority = svm.create_funded_account(10_000_000_000).unwrap();
336        let mint = svm.create_token_mint(&authority, 9).unwrap();
337
338        // New mint should have 0 supply
339        svm.assert_mint_supply(&mint.pubkey(), 0);
340    }
341
342    #[test]
343    fn test_assert_account_owner() {
344        let mut svm = LiteSVM::new();
345        let owner = svm.create_funded_account(10_000_000_000).unwrap();
346        let mint = svm.create_token_mint(&owner, 9).unwrap();
347
348        // Mint should be owned by token program
349        svm.assert_account_owner(&mint.pubkey(), &spl_token::id());
350    }
351
352    #[test]
353    #[should_panic(expected = "Account owner mismatch")]
354    fn test_assert_account_owner_fails() {
355        let mut svm = LiteSVM::new();
356        let owner = svm.create_funded_account(10_000_000_000).unwrap();
357        let mint = svm.create_token_mint(&owner, 9).unwrap();
358
359        // Should panic when expecting wrong owner
360        let wrong_owner = Pubkey::new_unique();
361        svm.assert_account_owner(&mint.pubkey(), &wrong_owner);
362    }
363
364    #[test]
365    fn test_assert_account_data_len() {
366        let mut svm = LiteSVM::new();
367        let owner = svm.create_funded_account(10_000_000_000).unwrap();
368        let mint = svm.create_token_mint(&owner, 9).unwrap();
369
370        // Mint account data is 82 bytes
371        svm.assert_account_data_len(&mint.pubkey(), 82);
372    }
373
374    #[test]
375    #[should_panic(expected = "Account data length mismatch")]
376    fn test_assert_account_data_len_fails() {
377        let mut svm = LiteSVM::new();
378        let owner = svm.create_funded_account(10_000_000_000).unwrap();
379        let mint = svm.create_token_mint(&owner, 9).unwrap();
380
381        // Should panic when expecting wrong length
382        svm.assert_account_data_len(&mint.pubkey(), 100);
383    }
384
385    #[test]
386    fn test_assert_account_data_len_token_account() {
387        let mut svm = LiteSVM::new();
388        let owner = svm.create_funded_account(10_000_000_000).unwrap();
389        let mint = svm.create_token_mint(&owner, 9).unwrap();
390        let token_account = svm.create_token_account(&mint.pubkey(), &owner).unwrap();
391
392        // Token account data is 165 bytes
393        svm.assert_account_data_len(&token_account.pubkey(), 165);
394    }
395}