litesvm_utils/
builder.rs

1//! Builder pattern for simplified test environment setup
2//!
3//! This module provides a fluent API for setting up test environments
4//! with automatic program deployment and configuration.
5
6use litesvm::LiteSVM;
7use solana_program::pubkey::Pubkey;
8
9/// Builder for creating a LiteSVM instance with programs pre-deployed
10///
11/// This provides a more ergonomic way to set up test environments compared to
12/// manually creating LiteSVM instances and deploying programs.
13///
14/// # Example
15/// ```ignore
16/// use litesvm_utils::LiteSVMBuilder;
17/// use solana_program::pubkey::Pubkey;
18///
19/// // Simple single program setup
20/// let program_id = Pubkey::new_unique();
21/// let program_bytes = include_bytes!("../target/deploy/my_program.so");
22/// let mut svm = LiteSVMBuilder::new()
23///     .deploy_program(program_id, program_bytes)
24///     .build();
25///
26/// // Or use the convenience method for single program
27/// let mut svm = LiteSVMBuilder::build_with_program(program_id, program_bytes);
28/// ```
29pub struct LiteSVMBuilder {
30    svm: LiteSVM,
31    programs: Vec<(Pubkey, Vec<u8>)>,
32}
33
34impl LiteSVMBuilder {
35    /// Create a new test environment builder
36    pub fn new() -> Self {
37        Self {
38            svm: LiteSVM::new(),
39            programs: Vec::new(),
40        }
41    }
42
43    /// Add a program to be deployed
44    ///
45    /// Programs are deployed in the order they are added.
46    ///
47    /// # Arguments
48    ///
49    /// * `program_id` - The program ID to deploy at
50    /// * `program_bytes` - The compiled program bytes (.so file contents)
51    ///
52    /// # Example
53    ///
54    /// ```ignore
55    /// builder.deploy_program(program_id, program_bytes)
56    /// ```
57    pub fn deploy_program(mut self, program_id: Pubkey, program_bytes: &[u8]) -> Self {
58        self.programs.push((program_id, program_bytes.to_vec()));
59        self
60    }
61
62    /// Build the LiteSVM instance with all programs deployed
63    ///
64    /// # Returns
65    ///
66    /// Returns the configured LiteSVM instance with all programs deployed
67    ///
68    /// # Example
69    ///
70    /// ```ignore
71    /// let mut svm = builder.build();
72    /// ```
73    pub fn build(mut self) -> LiteSVM {
74        // Deploy all programs
75        for (program_id, program_bytes) in self.programs {
76            self.svm.add_program(program_id, &program_bytes)
77                .expect("Failed to add program");
78        }
79
80        self.svm
81    }
82
83    /// Convenience method to quickly set up a single program
84    ///
85    /// This is equivalent to:
86    /// ```ignore
87    /// LiteSVMBuilder::new()
88    ///     .deploy_program(program_id, program_bytes)
89    ///     .build()
90    /// ```
91    ///
92    /// # Arguments
93    ///
94    /// * `program_id` - The program ID to deploy at
95    /// * `program_bytes` - The compiled program bytes
96    ///
97    /// # Returns
98    ///
99    /// Returns a configured LiteSVM instance with the program deployed
100    ///
101    /// # Example
102    ///
103    /// ```ignore
104    /// let mut svm = LiteSVMBuilder::build_with_program(program_id, program_bytes);
105    /// ```
106    pub fn build_with_program(program_id: Pubkey, program_bytes: &[u8]) -> LiteSVM {
107        Self::new()
108            .deploy_program(program_id, program_bytes)
109            .build()
110    }
111
112    /// Convenience method to quickly set up multiple programs
113    ///
114    /// # Arguments
115    ///
116    /// * `programs` - A slice of (program_id, program_bytes) tuples
117    ///
118    /// # Returns
119    ///
120    /// Returns a configured LiteSVM instance with all programs deployed
121    ///
122    /// # Example
123    ///
124    /// ```ignore
125    /// let programs = vec![
126    ///     (program_id1, program_bytes1),
127    ///     (program_id2, program_bytes2),
128    /// ];
129    /// let mut svm = LiteSVMBuilder::build_with_programs(&programs);
130    /// ```
131    pub fn build_with_programs(programs: &[(Pubkey, &[u8])]) -> LiteSVM {
132        let mut builder = Self::new();
133        for (program_id, program_bytes) in programs {
134            builder = builder.deploy_program(*program_id, program_bytes);
135        }
136        builder.build()
137    }
138}
139
140impl Default for LiteSVMBuilder {
141    fn default() -> Self {
142        Self::new()
143    }
144}
145
146/// Extension trait for LiteSVM to add program deployment capabilities
147pub trait ProgramTestExt {
148    /// Deploy a program to this LiteSVM instance
149    ///
150    /// # Example
151    /// ```no_run
152    /// # use litesvm_utils::ProgramTestExt;
153    /// # use litesvm::LiteSVM;
154    /// # use solana_program::pubkey::Pubkey;
155    /// # let mut svm = LiteSVM::new();
156    /// # let program_id = Pubkey::new_unique();
157    /// # let program_bytes = vec![];
158    /// svm.deploy_program(program_id, &program_bytes);
159    /// ```
160    fn deploy_program(&mut self, program_id: Pubkey, program_bytes: &[u8]);
161}
162
163impl ProgramTestExt for LiteSVM {
164    fn deploy_program(&mut self, program_id: Pubkey, program_bytes: &[u8]) {
165        self.add_program(program_id, program_bytes)
166            .expect("Failed to deploy program");
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173
174    #[test]
175    fn test_builder_new() {
176        let builder = LiteSVMBuilder::new();
177        let _svm = builder.build();
178        // Should successfully create a new LiteSVM instance
179    }
180
181    #[test]
182    fn test_builder_default() {
183        let builder = LiteSVMBuilder::default();
184        let _svm = builder.build();
185        // Default should work the same as new()
186    }
187
188    #[test]
189    fn test_builder_deploy_single_program() {
190        let program_id = Pubkey::new_unique();
191        let program_bytes = vec![1, 2, 3, 4];
192
193        // Test that builder fluent API works - don't call build() to avoid validation
194        let mut builder = LiteSVMBuilder::new();
195        builder = builder.deploy_program(program_id, &program_bytes);
196
197        // Verify the program was added to the builder
198        assert_eq!(builder.programs.len(), 1);
199        assert_eq!(builder.programs[0].0, program_id);
200    }
201
202    #[test]
203    fn test_builder_deploy_multiple_programs() {
204        let program_id1 = Pubkey::new_unique();
205        let program_id2 = Pubkey::new_unique();
206        let program_bytes = vec![1, 2, 3, 4];
207
208        // Test that builder accepts multiple programs
209        let builder = LiteSVMBuilder::new()
210            .deploy_program(program_id1, &program_bytes)
211            .deploy_program(program_id2, &program_bytes);
212
213        // Verify both programs were added
214        assert_eq!(builder.programs.len(), 2);
215    }
216
217    #[test]
218    fn test_build_with_programs_empty_list() {
219        let programs: Vec<(Pubkey, &[u8])> = vec![];
220        let _svm = LiteSVMBuilder::build_with_programs(&programs);
221        // Should not panic with empty program list
222    }
223
224    #[test]
225    fn test_builder_chaining() {
226        let program_id1 = Pubkey::new_unique();
227        let program_id2 = Pubkey::new_unique();
228        let program_id3 = Pubkey::new_unique();
229        let program_bytes = vec![1, 2, 3, 4];
230
231        // Test that builder methods can be chained
232        let builder = LiteSVMBuilder::new()
233            .deploy_program(program_id1, &program_bytes)
234            .deploy_program(program_id2, &program_bytes)
235            .deploy_program(program_id3, &program_bytes);
236
237        // Verify all 3 programs were added
238        assert_eq!(builder.programs.len(), 3);
239    }
240}