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}