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        }
78
79        self.svm
80    }
81
82    /// Convenience method to quickly set up a single program
83    ///
84    /// This is equivalent to:
85    /// ```ignore
86    /// LiteSVMBuilder::new()
87    ///     .deploy_program(program_id, program_bytes)
88    ///     .build()
89    /// ```
90    ///
91    /// # Arguments
92    ///
93    /// * `program_id` - The program ID to deploy at
94    /// * `program_bytes` - The compiled program bytes
95    ///
96    /// # Returns
97    ///
98    /// Returns a configured LiteSVM instance with the program deployed
99    ///
100    /// # Example
101    ///
102    /// ```ignore
103    /// let mut svm = LiteSVMBuilder::build_with_program(program_id, program_bytes);
104    /// ```
105    pub fn build_with_program(program_id: Pubkey, program_bytes: &[u8]) -> LiteSVM {
106        Self::new()
107            .deploy_program(program_id, program_bytes)
108            .build()
109    }
110
111    /// Convenience method to quickly set up multiple programs
112    ///
113    /// # Arguments
114    ///
115    /// * `programs` - A slice of (program_id, program_bytes) tuples
116    ///
117    /// # Returns
118    ///
119    /// Returns a configured LiteSVM instance with all programs deployed
120    ///
121    /// # Example
122    ///
123    /// ```ignore
124    /// let programs = vec![
125    ///     (program_id1, program_bytes1),
126    ///     (program_id2, program_bytes2),
127    /// ];
128    /// let mut svm = LiteSVMBuilder::build_with_programs(&programs);
129    /// ```
130    pub fn build_with_programs(programs: &[(Pubkey, &[u8])]) -> LiteSVM {
131        let mut builder = Self::new();
132        for (program_id, program_bytes) in programs {
133            builder = builder.deploy_program(*program_id, program_bytes);
134        }
135        builder.build()
136    }
137}
138
139impl Default for LiteSVMBuilder {
140    fn default() -> Self {
141        Self::new()
142    }
143}
144
145/// Extension trait for LiteSVM to add program deployment capabilities
146pub trait ProgramTestExt {
147    /// Deploy a program to this LiteSVM instance
148    ///
149    /// # Example
150    /// ```no_run
151    /// # use litesvm_utils::ProgramTestExt;
152    /// # use litesvm::LiteSVM;
153    /// # use solana_program::pubkey::Pubkey;
154    /// # let mut svm = LiteSVM::new();
155    /// # let program_id = Pubkey::new_unique();
156    /// # let program_bytes = vec![];
157    /// svm.deploy_program(program_id, &program_bytes);
158    /// ```
159    fn deploy_program(&mut self, program_id: Pubkey, program_bytes: &[u8]);
160}
161
162impl ProgramTestExt for LiteSVM {
163    fn deploy_program(&mut self, program_id: Pubkey, program_bytes: &[u8]) {
164        self.add_program(program_id, program_bytes);
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn test_builder_new() {
174        let builder = LiteSVMBuilder::new();
175        let _svm = builder.build();
176        // Should successfully create a new LiteSVM instance
177    }
178
179    #[test]
180    fn test_builder_default() {
181        let builder = LiteSVMBuilder::default();
182        let _svm = builder.build();
183        // Default should work the same as new()
184    }
185
186    #[test]
187    fn test_builder_deploy_single_program() {
188        let program_id = Pubkey::new_unique();
189        let program_bytes = vec![1, 2, 3, 4];
190
191        // Test that builder fluent API works - don't call build() to avoid validation
192        let mut builder = LiteSVMBuilder::new();
193        builder = builder.deploy_program(program_id, &program_bytes);
194
195        // Verify the program was added to the builder
196        assert_eq!(builder.programs.len(), 1);
197        assert_eq!(builder.programs[0].0, program_id);
198    }
199
200    #[test]
201    fn test_builder_deploy_multiple_programs() {
202        let program_id1 = Pubkey::new_unique();
203        let program_id2 = Pubkey::new_unique();
204        let program_bytes = vec![1, 2, 3, 4];
205
206        // Test that builder accepts multiple programs
207        let builder = LiteSVMBuilder::new()
208            .deploy_program(program_id1, &program_bytes)
209            .deploy_program(program_id2, &program_bytes);
210
211        // Verify both programs were added
212        assert_eq!(builder.programs.len(), 2);
213    }
214
215    #[test]
216    fn test_build_with_programs_empty_list() {
217        let programs: Vec<(Pubkey, &[u8])> = vec![];
218        let _svm = LiteSVMBuilder::build_with_programs(&programs);
219        // Should not panic with empty program list
220    }
221
222    #[test]
223    fn test_builder_chaining() {
224        let program_id1 = Pubkey::new_unique();
225        let program_id2 = Pubkey::new_unique();
226        let program_id3 = Pubkey::new_unique();
227        let program_bytes = vec![1, 2, 3, 4];
228
229        // Test that builder methods can be chained
230        let builder = LiteSVMBuilder::new()
231            .deploy_program(program_id1, &program_bytes)
232            .deploy_program(program_id2, &program_bytes)
233            .deploy_program(program_id3, &program_bytes);
234
235        // Verify all 3 programs were added
236        assert_eq!(builder.programs.len(), 3);
237    }
238}