sentry_core/
test.rs

1//! This provides testing functionality for building tests.
2//!
3//! **Feature:** `test` (*disabled by default*)
4//!
5//! If the sentry crate has been compiled with the test support feature this
6//! module becomes available and provides functionality to capture events
7//! in a block.
8//!
9//! # Example usage
10//!
11//! ```
12//! use sentry::test::with_captured_events;
13//! use sentry::{capture_message, Level};
14//!
15//! let events = with_captured_events(|| {
16//!     capture_message("Hello World!", Level::Warning);
17//! });
18//! assert_eq!(events.len(), 1);
19//! assert_eq!(events[0].message.as_ref().unwrap(), "Hello World!");
20//! ```
21
22use std::sync::{Arc, Mutex};
23
24use once_cell::sync::Lazy;
25
26use crate::protocol::Event;
27use crate::types::Dsn;
28use crate::{ClientOptions, Envelope, Hub, Transport};
29
30static TEST_DSN: Lazy<Dsn> = Lazy::new(|| "https://public@sentry.invalid/1".parse().unwrap());
31
32/// Collects events instead of sending them.
33///
34/// # Examples
35///
36/// ```
37/// use sentry::test::TestTransport;
38/// use sentry::{ClientOptions, Hub};
39/// use std::sync::Arc;
40///
41/// let transport = TestTransport::new();
42/// let options = ClientOptions {
43///     dsn: Some("https://public@example.com/1".parse().unwrap()),
44///     transport: Some(Arc::new(transport.clone())),
45///     ..ClientOptions::default()
46/// };
47/// Hub::current().bind_client(Some(Arc::new(options.into())));
48/// ```
49pub struct TestTransport {
50    collected: Mutex<Vec<Envelope>>,
51}
52
53impl TestTransport {
54    /// Creates a new test transport.
55    #[allow(clippy::new_ret_no_self)]
56    pub fn new() -> Arc<TestTransport> {
57        Arc::new(TestTransport {
58            collected: Mutex::new(vec![]),
59        })
60    }
61
62    /// Fetches and clears the contained events.
63    pub fn fetch_and_clear_events(&self) -> Vec<Event<'static>> {
64        self.fetch_and_clear_envelopes()
65            .into_iter()
66            .filter_map(|envelope| envelope.event().cloned())
67            .collect()
68    }
69
70    /// Fetches and clears the contained envelopes.
71    pub fn fetch_and_clear_envelopes(&self) -> Vec<Envelope> {
72        let mut guard = self.collected.lock().unwrap();
73        std::mem::take(&mut *guard)
74    }
75}
76
77impl Transport for TestTransport {
78    fn send_envelope(&self, envelope: Envelope) {
79        self.collected.lock().unwrap().push(envelope);
80    }
81}
82
83/// Runs some code with the default test hub and returns the captured events.
84///
85/// See [`with_captured_envelopes_options`](fn.with_captured_envelopes_options.html)
86pub fn with_captured_events<F: FnOnce()>(f: F) -> Vec<Event<'static>> {
87    with_captured_events_options(f, ClientOptions::default())
88}
89
90/// Runs some code with the default test hub with the given options and
91/// returns the captured events.
92///
93/// See [`with_captured_envelopes_options`](fn.with_captured_envelopes_options.html)
94pub fn with_captured_events_options<F: FnOnce(), O: Into<ClientOptions>>(
95    f: F,
96    options: O,
97) -> Vec<Event<'static>> {
98    with_captured_envelopes_options(f, options)
99        .into_iter()
100        .filter_map(|envelope| envelope.event().cloned())
101        .collect()
102}
103
104/// Runs some code with the default test hub and returns the captured envelopes.
105///
106/// See [`with_captured_envelopes_options`](fn.with_captured_envelopes_options.html)
107pub fn with_captured_envelopes<F: FnOnce()>(f: F) -> Vec<Envelope> {
108    with_captured_envelopes_options(f, ClientOptions::default())
109}
110
111/// Runs some code with the default test hub with the given options and
112/// returns the captured envelopes.
113///
114/// If no DSN is set on the options a default test DSN is inserted.  The
115/// transport on the options is also overridden with a `TestTransport`.
116///
117/// This is a shortcut for creating a testable client with the supplied options
118/// and `TestTransport`, and bind it to a newly created hub for the duration of
119/// the call.
120pub fn with_captured_envelopes_options<F: FnOnce(), O: Into<ClientOptions>>(
121    f: F,
122    options: O,
123) -> Vec<Envelope> {
124    let transport = TestTransport::new();
125    let mut options = options.into();
126    options.dsn = Some(options.dsn.unwrap_or_else(|| TEST_DSN.clone()));
127    options.transport = Some(Arc::new(transport.clone()));
128    Hub::run(
129        Arc::new(Hub::new(
130            Some(Arc::new(options.into())),
131            Arc::new(Default::default()),
132        )),
133        f,
134    );
135    transport.fetch_and_clear_envelopes()
136}