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