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}