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}