wae_testing/
environment.rs1use crate::error::{TestingError, TestingResult};
4use parking_lot::RwLock;
5use std::{collections::HashMap, sync::Arc, time::Duration};
6
7#[derive(Debug, Clone)]
9pub struct TestEnvConfig {
10 pub name: String,
12 pub enable_logging: bool,
14 pub enable_tracing: bool,
16 pub default_timeout: Duration,
18 pub custom: HashMap<String, String>,
20}
21
22impl Default for TestEnvConfig {
23 fn default() -> Self {
24 Self {
25 name: "test".to_string(),
26 enable_logging: true,
27 enable_tracing: false,
28 default_timeout: Duration::from_secs(30),
29 custom: HashMap::new(),
30 }
31 }
32}
33
34impl TestEnvConfig {
35 pub fn new() -> Self {
37 Self::default()
38 }
39
40 pub fn name(mut self, name: impl Into<String>) -> Self {
42 self.name = name.into();
43 self
44 }
45
46 pub fn with_logging(mut self, enable: bool) -> Self {
48 self.enable_logging = enable;
49 self
50 }
51
52 pub fn with_tracing(mut self, enable: bool) -> Self {
54 self.enable_tracing = enable;
55 self
56 }
57
58 pub fn timeout(mut self, timeout: Duration) -> Self {
60 self.default_timeout = timeout;
61 self
62 }
63
64 pub fn custom(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
66 self.custom.insert(key.into(), value.into());
67 self
68 }
69}
70
71#[derive(Debug, Clone, PartialEq)]
73pub enum TestEnvState {
74 Uninitialized,
76 Initialized,
78 Destroyed,
80}
81
82pub struct TestEnv {
84 config: TestEnvConfig,
86 state: Arc<RwLock<TestEnvState>>,
88 #[allow(clippy::type_complexity)]
90 cleanup_handlers: Arc<RwLock<Vec<Box<dyn Fn() + Send + Sync>>>>,
91 storage: Arc<RwLock<HashMap<String, Box<dyn std::any::Any + Send + Sync>>>>,
93}
94
95impl TestEnv {
96 pub fn new(config: TestEnvConfig) -> Self {
98 Self {
99 config,
100 state: Arc::new(RwLock::new(TestEnvState::Uninitialized)),
101 cleanup_handlers: Arc::new(RwLock::new(Vec::new())),
102 storage: Arc::new(RwLock::new(HashMap::new())),
103 }
104 }
105
106 pub fn default_env() -> Self {
108 Self::new(TestEnvConfig::default())
109 }
110
111 pub fn setup(&self) -> TestingResult<()> {
113 let mut state = self.state.write();
114 if *state != TestEnvState::Uninitialized {
115 return Err(TestingError::EnvironmentError("Environment already initialized".to_string()));
116 }
117
118 *state = TestEnvState::Initialized;
119 Ok(())
120 }
121
122 pub fn teardown(&self) -> TestingResult<()> {
124 let mut state = self.state.write();
125 if *state != TestEnvState::Initialized {
126 return Err(TestingError::EnvironmentError("Environment not initialized".to_string()));
127 }
128
129 let handlers = self.cleanup_handlers.write();
130 for handler in handlers.iter() {
131 handler();
132 }
133
134 self.storage.write().clear();
135
136 *state = TestEnvState::Destroyed;
137 Ok(())
138 }
139
140 pub fn state(&self) -> TestEnvState {
142 self.state.read().clone()
143 }
144
145 pub fn on_cleanup<F>(&self, handler: F)
147 where
148 F: Fn() + Send + Sync + 'static,
149 {
150 self.cleanup_handlers.write().push(Box::new(handler));
151 }
152
153 pub fn set<T: 'static + Send + Sync>(&self, key: &str, value: T) {
155 self.storage.write().insert(key.to_string(), Box::new(value));
156 }
157
158 pub fn get<T: 'static + Clone>(&self, key: &str) -> Option<T> {
160 let storage = self.storage.read();
161 storage.get(key).and_then(|v| v.downcast_ref::<T>().cloned())
162 }
163
164 pub fn config(&self) -> &TestEnvConfig {
166 &self.config
167 }
168
169 pub async fn with_fixture<F, R>(&self, fixture: F) -> TestingResult<R>
171 where
172 F: FnOnce() -> TestingResult<R>,
173 {
174 self.setup()?;
175
176 let fixture_data = fixture()?;
177
178 self.teardown()?;
179
180 Ok(fixture_data)
181 }
182
183 pub async fn run_test<F, Fut>(&self, test: F) -> TestingResult<()>
185 where
186 F: FnOnce() -> Fut,
187 Fut: std::future::Future<Output = TestingResult<()>>,
188 {
189 self.setup()?;
190
191 let result = test().await;
192
193 self.teardown()?;
194
195 result
196 }
197}
198
199pub struct TestEnvBuilder {
201 config: TestEnvConfig,
202}
203
204impl TestEnvBuilder {
205 pub fn new() -> Self {
207 Self { config: TestEnvConfig::default() }
208 }
209
210 pub fn name(mut self, name: impl Into<String>) -> Self {
212 self.config.name = name.into();
213 self
214 }
215
216 pub fn with_logging(mut self, enable: bool) -> Self {
218 self.config.enable_logging = enable;
219 self
220 }
221
222 pub fn with_tracing(mut self, enable: bool) -> Self {
224 self.config.enable_tracing = enable;
225 self
226 }
227
228 pub fn timeout(mut self, timeout: Duration) -> Self {
230 self.config.default_timeout = timeout;
231 self
232 }
233
234 pub fn custom(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
236 self.config.custom.insert(key.into(), value.into());
237 self
238 }
239
240 pub fn build(self) -> TestEnv {
242 TestEnv::new(self.config)
243 }
244}
245
246impl Default for TestEnvBuilder {
247 fn default() -> Self {
248 Self::new()
249 }
250}
251
252pub fn create_test_env() -> TestEnv {
254 TestEnv::default_env()
255}
256
257pub fn create_test_env_with_config(config: TestEnvConfig) -> TestEnv {
259 TestEnv::new(config)
260}