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