Skip to main content

spire_core/testing/
visit.rs

1use std::fmt;
2use std::sync::Arc;
3
4use super::MockSite;
5use crate::backend::Backend;
6use crate::dataset::{Data, Dataset, Datasets};
7use crate::frontier::queue::Queue;
8use crate::frontier::seed::Seed;
9use crate::frontier::{Frontier, InMemFrontier};
10use crate::worker::{Outcome, Worker};
11use crate::{Context, Result};
12
13/// One request to a [`MockSite`], run through a worker without crawling the
14/// links it queues.
15///
16/// Created by [`MockSite::visit`]. After [`run`](Self::run), the worker's
17/// effects can be checked: the [`Outcome`] it returned, the seeds it
18/// [`queued`](Self::queued), and the items it wrote to its datasets.
19///
20/// The queue has no facades: no deduplication, filters or depth limits, so
21/// every link the worker follows is kept.
22pub struct Visit {
23    site: MockSite,
24    seed: Seed,
25    frontier: Arc<InMemFrontier>,
26    datasets: Arc<Datasets>,
27}
28
29impl Visit {
30    pub(super) fn new(site: MockSite, seed: Seed) -> Self {
31        Self {
32            site,
33            seed,
34            frontier: Arc::new(InMemFrontier::fifo()),
35            datasets: Arc::default(),
36        }
37    }
38
39    /// Registers `dataset` for items of type `T`.
40    #[must_use]
41    pub fn with_dataset<T, D>(mut self, dataset: D) -> Self
42    where
43        T: Send + 'static,
44        D: Dataset<T>,
45    {
46        Arc::make_mut(&mut self.datasets).insert(dataset);
47        self
48    }
49
50    /// Fetches the request and runs `worker` on it, returning its outcome.
51    ///
52    /// Each run is a new request: running twice fetches twice, and adds to
53    /// the same queue and datasets.
54    pub async fn run<W: Worker<<MockSite as Backend>::Session>>(&self, worker: &W) -> Outcome {
55        let client = match self.site.connect().await {
56            Ok(client) => client,
57            Err(error) => return Outcome::Fail(error),
58        };
59        let request = self.seed.clone().into_request();
60        let queue = Queue::new(Arc::clone(&self.frontier)).child_of(&request);
61        let cx = Context::from_parts(request, client, queue, Arc::clone(&self.datasets));
62        worker.handle(cx).await
63    }
64
65    /// Returns the seeds queued so far, in order.
66    #[must_use]
67    pub fn queued(&self) -> Vec<Seed> {
68        self.frontier.snapshot().unwrap_or_default()
69    }
70
71    /// Returns the dataset for items of type `T`.
72    ///
73    /// # Errors
74    ///
75    /// Returns a [`Dataset`](crate::ErrorKind::Dataset) error if no dataset
76    /// is registered for `T`.
77    pub fn dataset<T: Send + 'static>(&self) -> Result<Data<T>> {
78        self.datasets.get()
79    }
80}
81
82impl fmt::Debug for Visit {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        f.debug_struct("Visit")
85            .field("seed", &self.seed)
86            .field("queued", &self.frontier.size_hint())
87            .finish_non_exhaustive()
88    }
89}