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