workspacer_workspace_mock/
mock.rs1crate::ix!();
3
4lazy_static! {
5 static ref MOCK_WORKSPACE_REGISTRY: AsyncMutex<HashMap<PathBuf, Box<dyn std::any::Any + Send + Sync>>>
10 = AsyncMutex::new(HashMap::new());
11}
12
13impl MockWorkspace<PathBuf, MockCrateHandle> {
14 pub async fn register_in_global(&self) {
18 let path: PathBuf = self.path().to_path_buf();
19 trace!("Registering MockWorkspace<PathBuf,MockCrateHandle> in global map, path={:?}", path);
20 let mut lock = MOCK_WORKSPACE_REGISTRY.lock().await;
21 lock.insert(path.clone(), Box::new(self.clone()));
23 info!("MockWorkspace<PathBuf,MockCrateHandle> with path={:?} has been registered", path);
24 }
25}
26
27#[derive(Builder, MutGetters, Getters, Debug, Clone)]
28#[builder(setter(into))]
29#[getset(get = "pub", get_mut = "pub")]
30pub struct MockWorkspace<P, H>
31where
32 H: Clone + CrateHandleInterface<P>,
33 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
34{
35 path: P,
38
39 #[builder(default = "Vec::new()")]
43 crates: Vec<Arc<AsyncMutex<H>>>,
44
45 #[builder(default = "false")]
49 simulate_missing_cargo_toml: bool,
50
51 #[builder(default = "false")]
56 simulate_not_a_workspace: bool,
57
58 #[builder(default = "false")]
61 simulate_failed_integrity: bool,
62
63 #[builder(default = "false")]
66 simulate_no_crates: bool,
67}
68
69impl<P, H> MockWorkspace<P, H>
70where
71 H: Clone + CrateHandleInterface<P>,
72 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Clone + Send + Sync + 'async_trait,
73{
74 pub fn fully_valid_config() -> Self {
82 trace!("MockWorkspace::fully_valid_config constructor called");
83 let crates_list = vec![];
96
97 MockWorkspaceBuilder::default()
99 .path(PathBuf::from("/fake/mock/workspace/path")) .crates(crates_list)
101 .simulate_missing_cargo_toml(false)
102 .simulate_not_a_workspace(false)
103 .simulate_failed_integrity(false)
104 .simulate_no_crates(false)
105 .build()
106 .unwrap()
107 }
108}
109
110#[async_trait]
111impl<P, H> AsyncTryFrom<P> for MockWorkspace<P, H>
112where
113 for<'async_trait> H: Clone + CrateHandleInterface<P> + Send + Sync + 'async_trait,
114 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
115{
116 type Error = WorkspaceError;
117
118 async fn new(path: &P) -> Result<Self, Self::Error> {
119 trace!("MockWorkspace::AsyncTryFrom::new called, path={:?}", path.as_ref());
120 let path_buf = path.as_ref().to_path_buf();
121
122 {
125 let lock = MOCK_WORKSPACE_REGISTRY.lock().await;
126 if let Some(boxed_any) = lock.get(&path_buf) {
127 debug!("Found a pre-registered Box<dyn Any> for path={:?}", path_buf);
128 if let Some(existing_ws) = boxed_any.downcast_ref::<MockWorkspace<P, H>>() {
129 trace!("Successfully downcast to MockWorkspace<P,H>, applying simulation checks");
130
131 if *existing_ws.simulate_missing_cargo_toml() {
132 error!("simulate_missing_cargo_toml=true => returning InvalidWorkspace");
133 return Err(WorkspaceError::InvalidWorkspace {
134 invalid_workspace_path: path_buf,
135 });
136 }
137
138 if *existing_ws.simulate_not_a_workspace() {
139 error!("simulate_not_a_workspace=true => returning ActuallyInSingleCrate");
140 return Err(WorkspaceError::ActuallyInSingleCrate {
141 path: path_buf,
142 });
143 }
144
145 info!("Returning a clone of the pre-registered MockWorkspace<P,H>");
146 return Ok(existing_ws.clone());
147 } else {
148 warn!("Failed downcasting pre-registered item to MockWorkspace<P,H>; ignoring it");
149 }
150 }
151 }
152
153 let mut ws = Self::fully_valid_config();
155 ws.path = path.clone();
156
157 if *ws.simulate_missing_cargo_toml() {
158 error!("simulate_missing_cargo_toml=true => returning InvalidWorkspace");
159 return Err(WorkspaceError::InvalidWorkspace {
160 invalid_workspace_path: path_buf,
161 });
162 }
163
164 if *ws.simulate_not_a_workspace() {
165 error!("simulate_not_a_workspace=true => returning ActuallyInSingleCrate");
166 return Err(WorkspaceError::ActuallyInSingleCrate {
167 path: path_buf,
168 });
169 }
170
171 info!("Returning a new fully_valid_config-based MockWorkspace<P,H>");
172 Ok(ws)
173 }
174}
175
176impl<P, H> WorkspaceInterface<P, H> for MockWorkspace<P, H>
177where
178 for<'async_trait> H: Clone + CrateHandleInterface<P> + 'async_trait,
179 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
180{
181 }
183
184impl<P, H> GetCrates<P, H> for MockWorkspace<P, H>
185where
186 H: Clone + CrateHandleInterface<P>,
187 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
188{
189 fn crates(&self) -> &[Arc<AsyncMutex<H>>] {
190 trace!("MockWorkspace::GetCrates::crates called");
191 self.crates()
192 }
193}
194
195impl<P, H> GetCratesMut<P, H> for MockWorkspace<P, H>
196where
197 H: Clone + CrateHandleInterface<P>,
198 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
199{
200 fn crates_mut(&mut self) -> &mut Vec<Arc<AsyncMutex<H>>> {
201 trace!("MockWorkspace::GetCratesMut::crates called");
202 self.crates_mut()
203 }
204}
205
206impl<P, H> NumCrates for MockWorkspace<P, H>
207where
208 H: Clone + CrateHandleInterface<P>,
209 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
210{
211 fn n_crates(&self) -> usize {
212 trace!("MockWorkspace::NumCrates::n_crates called");
213 self.crates().len()
214 }
215}
216
217#[async_trait]
218impl<P, H> ValidateIntegrity for MockWorkspace<P, H>
219where
220 H: Clone + CrateHandleInterface<P>,
221 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
222{
223 type Error = WorkspaceError;
224
225 async fn validate_integrity(&self) -> Result<(), Self::Error> {
226 trace!("MockWorkspace::validate_integrity called");
227
228 if *self.simulate_missing_cargo_toml() {
230 error!("simulate_missing_cargo_toml=true => returning InvalidWorkspace");
231 return Err(WorkspaceError::InvalidWorkspace {
232 invalid_workspace_path: self.path().as_ref().to_path_buf(),
233 });
234 }
235
236 if *self.simulate_not_a_workspace() {
238 error!("simulate_not_a_workspace=true => returning ActuallyInSingleCrate");
239 return Err(WorkspaceError::ActuallyInSingleCrate {
240 path: self.path().as_ref().to_path_buf(),
241 });
242 }
243
244 if *self.simulate_failed_integrity() {
246 error!("simulate_failed_integrity=true => returning InvalidWorkspace");
247 return Err(WorkspaceError::InvalidWorkspace {
248 invalid_workspace_path: self.path().as_ref().to_path_buf(),
249 });
250 }
251
252 for c in self.crates().iter() {
257 let guard = c.lock().await;
258 guard.validate_integrity().await?;
259 }
260
261 info!("MockWorkspace: integrity validation passed");
262 Ok(())
263 }
264}
265
266#[async_trait]
267impl<P, H> FindCrateByName<P, H> for MockWorkspace<P, H>
268where
269 H: Clone + CrateHandleInterface<P>,
270 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
271{
272 async fn find_crate_by_name(&self, name: &str) -> Option<Arc<AsyncMutex<H>>> {
273 trace!("MockWorkspace::FindCrateByName::find_crate_by_name called, name={}", name);
274 for crate_arc in self.crates().iter() {
275 let guard = crate_arc.lock().await;
276 if guard.name() == name {
277 debug!("MockWorkspace: found crate matching name='{}'", name);
278 return Some(Arc::clone(crate_arc));
279 }
280 }
281 info!("MockWorkspace: no crate matching name='{}'", name);
282 None
283 }
284}
285
286#[async_trait]
287impl<P, H> GetAllCrateNames for MockWorkspace<P, H>
288where
289 H: Clone + CrateHandleInterface<P>,
290 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
291{
292 async fn get_all_crate_names(&self) -> Vec<String> {
293 trace!("MockWorkspace::GetAllCrateNames::get_all_crate_names called");
294 let mut names = vec![];
295 for crate_arc in self.crates().iter() {
296 let guard = crate_arc.lock().await;
297 names.push(guard.name().to_string());
298 }
299 info!("MockWorkspace: returning crate names: {:?}", names);
300 names
301 }
302}
303
304impl<P, H> AsRef<Path> for MockWorkspace<P, H>
305where
306 H: Clone + CrateHandleInterface<P>,
307 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
308{
309 fn as_ref(&self) -> &Path {
310 trace!("MockWorkspace::as_ref called, returning path={:?}", self.path().as_ref());
311 self.path().as_ref()
312 }
313}
314
315#[async_trait]
316impl<P, H> AsyncFindItems for MockWorkspace<P, H>
317where
318 H: Clone + CrateHandleInterface<P> + Send + Sync,
319 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
320{
321 type Item = Arc<AsyncMutex<H>>;
322 type Error = WorkspaceError;
323
324 async fn find_items(_path: &Path) -> Result<Vec<Self::Item>, Self::Error> {
325 trace!("MockWorkspace::AsyncFindItems::find_items called (static), ignoring path={:?}", _path);
326 info!("MockWorkspace: returning an empty crate list from find_items");
333 Ok(vec![])
334 }
335}
336
337#[async_trait]
338impl<P, H> AsyncPathValidator for MockWorkspace<P, H>
339where
340 H: Clone + CrateHandleInterface<P> + Send + Sync,
341 for<'async_trait> P: Clone + From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait,
342{
343 async fn is_valid(path: &Path) -> bool {
344 trace!("MockWorkspace::AsyncPathValidator::is_valid called for path={:?}", path);
345 debug!("MockWorkspace: returning true for is_valid(...) by default");
348 true
349 }
350}
351
352#[cfg(test)]
356mod test_mock_workspace {
357 use super::*;
358
359 #[traced_test]
363 fn test_fully_valid_config_works() {
364 let mock_ws = MockWorkspace::<PathBuf, MockCrateHandle>::fully_valid_config();
365 assert!(!mock_ws.simulate_missing_cargo_toml(), "Should default to false");
366 assert!(!mock_ws.simulate_not_a_workspace(), "Should default to false");
367 assert!(!mock_ws.simulate_failed_integrity(), "Should default to false");
368 assert!(!mock_ws.simulate_no_crates(), "Should default to false");
369
370 assert_eq!(mock_ws.n_crates(), 0, "Currently we have an empty crates list");
373 }
374
375 #[traced_test]
376 async fn test_mock_workspace_new_missing_cargo_toml_fails() {
377 let path = PathBuf::from("/fake/mock/workspace/path_for_missing_cargo_toml");
380
381 trace!("test_mock_workspace_new_missing_cargo_toml_fails starting");
382 let mut failing_ws = MockWorkspace::<PathBuf, MockCrateHandle>::fully_valid_config();
384 *failing_ws.path_mut() = path.clone();
386 *failing_ws.simulate_missing_cargo_toml_mut() = true;
388 failing_ws.register_in_global().await;
390
391 let result = MockWorkspace::<PathBuf, MockCrateHandle>::new(&path).await;
394
395 assert!(result.is_err(), "Should fail with simulate_missing_cargo_toml=true");
397 match result.err().unwrap() {
398 WorkspaceError::InvalidWorkspace { .. } => {
399 info!("Got expected WorkspaceError::InvalidWorkspace");
400 }
401 other => {
402 panic!("Expected InvalidWorkspace error, got: {:?}", other);
403 }
404 }
405 }
406
407 #[traced_test]
408 async fn test_mock_workspace_new_not_a_workspace_fails() {
409 let path = PathBuf::from("/fake/mock/workspace/path_for_not_a_workspace");
411
412 trace!("test_mock_workspace_new_not_a_workspace_fails starting");
413 let mut failing_ws = MockWorkspace::<PathBuf, MockCrateHandle>::fully_valid_config();
415 *failing_ws.path_mut() = path.clone();
417 *failing_ws.simulate_not_a_workspace_mut() = true;
419 failing_ws.register_in_global().await;
421
422 let result = MockWorkspace::<PathBuf, MockCrateHandle>::new(&path).await;
425 assert!(result.is_err(), "Should fail with simulate_not_a_workspace=true");
426
427 match result.err().unwrap() {
428 WorkspaceError::ActuallyInSingleCrate { .. } => {
429 info!("Got expected WorkspaceError::ActuallyInSingleCrate");
430 }
431 other => {
432 panic!("Expected ActuallyInSingleCrate error, got: {:?}", other);
433 }
434 }
435 }
436
437 #[traced_test]
438 async fn test_mock_workspace_validate_integrity_fails_when_simulated() {
439 let mut ws = MockWorkspace::<PathBuf, MockCrateHandle>::fully_valid_config();
440 *ws.simulate_failed_integrity_mut() = true;
441 let result = ws.validate_integrity().await;
442 assert!(result.is_err(), "Should fail if simulate_failed_integrity=true");
443 }
444
445 #[traced_test]
446 async fn test_mock_workspace_find_crate_by_name() {
447 let crate_a = Arc::new(AsyncMutex::new(
450 MockCrateHandle::fully_valid_config()
451 .to_builder()
452 .crate_name("crateA")
453 .build()
454 .unwrap()
455 ));
456 let crate_b = Arc::new(AsyncMutex::new(
457 MockCrateHandle::fully_valid_config()
458 .to_builder()
459 .crate_name("crateB")
460 .build()
461 .unwrap()
462 ));
463
464 let ws = MockWorkspaceBuilder::<PathBuf, MockCrateHandle>::default()
465 .path(PathBuf::from("/fake/mock/workspace/path"))
466 .crates(vec![crate_a.clone(), crate_b.clone()])
467 .build()
468 .unwrap();
469
470 assert_eq!(ws.n_crates(), 2, "We have 2 crates total");
471 let found = ws.find_crate_by_name("crateB").await;
472 assert!(found.is_some(), "Should find crateB by name");
473 let found = found.unwrap();
474 assert_eq!(found.lock().await.name(), "crateB");
475 }
476
477 #[traced_test]
478 async fn test_mock_workspace_get_all_crate_names() {
479 let crate_1 = Arc::new(AsyncMutex::new(
481 MockCrateHandle::fully_valid_config()
482 .to_builder()
483 .crate_name("crateAlpha")
484 .build()
485 .unwrap()
486 ));
487 let crate_2 = Arc::new(AsyncMutex::new(
488 MockCrateHandle::fully_valid_config()
489 .to_builder()
490 .crate_name("crateBeta")
491 .build()
492 .unwrap()
493 ));
494
495 let ws = MockWorkspaceBuilder::<PathBuf, MockCrateHandle>::default()
496 .path(PathBuf::from("/fake/mock/workspace/path"))
497 .crates(vec![crate_1.clone(), crate_2.clone()])
498 .build()
499 .unwrap();
500
501 let names = ws.get_all_crate_names().await;
502 assert_eq!(names.len(), 2, "Should have 2 names total");
503 assert!(names.contains(&"crateAlpha".to_string()));
504 assert!(names.contains(&"crateBeta".to_string()));
505 }
506}