anchor_litesvm/lib.rs
1//! # anchor-litesvm
2//!
3//! Testing framework for Anchor programs using LiteSVM.
4//!
5//! This crate provides a **simplified syntax similar to anchor-client** but without RPC overhead,
6//! achieving **78% code reduction** compared to raw LiteSVM.
7//!
8//! ## Why anchor-litesvm?
9//!
10//! | Feature | anchor-client + LiteSVM | anchor-litesvm |
11//! |---------|-------------------------|----------------|
12//! | **Code Lines** | 279 | **106 (78% less)** |
13//! | **Compilation** | Slow (network deps) | **40% faster** |
14//! | **Setup** | Mock RPC needed | **One line** |
15//! | **Syntax** | anchor-client | **Similar to anchor-client** |
16//! | **Helpers** | Manual | **Built-in** |
17//!
18//! ## Key Features
19//!
20//! - **Simplified Syntax**: Similar to anchor-client
21//! - **No Mock RPC Setup**: One-line initialization
22//! - **Integrated Test Helpers**: Token operations, assertions, event parsing
23//! - **Familiar API**: If you know anchor-client, you know this
24//! - **Transferable Knowledge**: Test skills apply to production
25//! - **Type Safety**: Compile-time validation with Anchor types
26//!
27//! ## Quick Start
28//!
29//! ```rust,ignore
30//! use anchor_litesvm::{AnchorLiteSVM, TestHelpers, AssertionHelpers, Signer};
31//!
32//! // 1. Generate client types from your program
33//! anchor_lang::declare_program!(my_program);
34//!
35//! #[test]
36//! fn test_my_program() {
37//! // 2. One-line setup (no mock RPC needed)
38//! let mut ctx = AnchorLiteSVM::build_with_program(
39//! my_program::ID,
40//! include_bytes!("../target/deploy/my_program.so"),
41//! );
42//!
43//! // 3. Create test accounts with helpers
44//! let user = ctx.svm.create_funded_account(10_000_000_000).unwrap();
45//! let mint = ctx.svm.create_token_mint(&user, 9).unwrap();
46//!
47//! // 4. Build instruction (simplified syntax - similar to anchor client)
48//! let ix = ctx.program()
49//! .accounts(my_program::client::accounts::Transfer {
50//! from: sender_account,
51//! to: recipient_account,
52//! authority: user.pubkey(),
53//! token_program: spl_token::id(),
54//! })
55//! .args(my_program::client::args::Transfer { amount: 100 })
56//! .instruction()?;
57//!
58//! // 5. Execute and verify
59//! ctx.execute_instruction(ix, &[&user])?.assert_success();
60//! ctx.svm.assert_token_balance(&recipient_account, 100);
61//! }
62//! ```
63//!
64//! ## Common Patterns
65//!
66//! ### Token Operations
67//!
68//! ```rust,ignore
69//! use litesvm_utils::TestHelpers;
70//!
71//! let mint = ctx.svm.create_token_mint(&authority, 9)?;
72//! let token_account = ctx.svm.create_associated_token_account(&mint.pubkey(), &owner)?;
73//! ctx.svm.mint_to(&mint.pubkey(), &token_account, &authority, 1_000_000)?;
74//! ```
75//!
76//! ### PDA Derivation
77//!
78//! ```rust,ignore
79//! // Just the address
80//! let pda = ctx.svm.get_pda(&[b"vault", user.pubkey().as_ref()], &program_id);
81//!
82//! // With bump seed
83//! let (pda, bump) = ctx.svm.get_pda_with_bump(&[b"vault"], &program_id);
84//! ```
85//!
86//! ### Error Testing
87//!
88//! ```rust,ignore
89//! let result = ctx.execute_instruction(ix, &[&user])?;
90//! result.assert_failure();
91//! result.assert_error("insufficient funds");
92//! result.assert_error_code(6000); // Anchor custom error
93//! ```
94//!
95//! ### Event Parsing
96//!
97//! ```rust,ignore
98//! use anchor_litesvm::EventHelpers;
99//!
100//! let events: Vec<TransferEvent> = result.parse_events()?;
101//! result.assert_event_emitted::<TransferEvent>();
102//! ```
103//!
104//! ### Account Deserialization
105//!
106//! ```rust,ignore
107//! let account: MyAccountType = ctx.get_account(&pda)?;
108//! assert_eq!(account.authority, user.pubkey());
109//! ```
110//!
111//! ## Documentation
112//!
113//! - [Quick Start Guide](https://github.com/brimigs/anchor-litesvm/blob/main/docs/QUICK_START.md)
114//! - [API Reference](https://github.com/brimigs/anchor-litesvm/blob/main/docs/API_REFERENCE.md)
115//! - [Migration Guide](https://github.com/brimigs/anchor-litesvm/blob/main/docs/MIGRATION.md)
116//! - [Examples](https://github.com/brimigs/anchor-litesvm/tree/main/examples)
117//!
118//! ## Modules
119//!
120//! - [`account`] - Account deserialization utilities
121//! - [`builder`] - Test environment builders
122//! - [`context`] - Main test context (`AnchorContext`)
123//! - [`events`] - Event parsing helpers
124//! - [`instruction`] - Instruction building utilities
125//! - [`program`] - Simplified Program API
126
127pub mod account;
128pub mod builder;
129pub mod context;
130pub mod events;
131pub mod instruction;
132pub mod program;
133
134// Re-export main types for convenience
135pub use account::{get_anchor_account, get_anchor_account_unchecked, AccountError};
136pub use builder::{AnchorLiteSVM, ProgramTestExt};
137pub use context::AnchorContext;
138pub use events::{parse_event_data, EventError, EventHelpers};
139pub use instruction::{build_anchor_instruction, calculate_anchor_discriminator};
140pub use program::{InstructionBuilder, Program};
141
142// Re-export litesvm-utils functionality for convenience
143pub use litesvm_utils::{
144 AssertionHelpers, LiteSVMBuilder, TestHelpers, TransactionError, TransactionHelpers,
145 TransactionResult,
146};
147
148// Re-export commonly used external types
149pub use anchor_lang::{AccountDeserialize, AnchorSerialize};
150pub use litesvm::LiteSVM;
151pub use solana_keypair::Keypair;
152pub use solana_program::instruction::{AccountMeta, Instruction};
153pub use solana_program::pubkey::Pubkey;
154pub use solana_signer::Signer;
155
156#[cfg(test)]
157mod integration_tests {
158 use super::*;
159 use borsh::BorshSerialize;
160
161 #[test]
162 fn test_full_workflow() {
163 // Create test context
164 let svm = LiteSVM::new();
165 let program_id = Pubkey::new_unique();
166 let _ctx = AnchorContext::new(svm, program_id);
167
168 // Test instruction building
169 // In anchor 1.0.0, AnchorSerialize is an alias for BorshSerialize
170 #[derive(BorshSerialize)]
171 struct TestArgs {
172 value: u64,
173 }
174
175 let accounts = vec![
176 AccountMeta::new(Pubkey::new_unique(), true),
177 AccountMeta::new_readonly(Pubkey::new_unique(), false),
178 ];
179
180 let instruction =
181 build_anchor_instruction(&program_id, "test", accounts, TestArgs { value: 42 })
182 .unwrap();
183
184 assert_eq!(instruction.program_id, program_id);
185 assert!(!instruction.data.is_empty());
186 }
187}