1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
//! This provides testing functionality for building tests.
//!
//! **Feature:** `test` (*disabled by default*)
//!
//! If the sentry crate has been compiled with the test support feature this
//! module becomes available and provides functionality to capture events
//! in a block.
//!
//! # Example usage
//!
//! ```
//! use sentry::test::with_captured_events;
//! use sentry::{capture_message, Level};
//!
//! let events = with_captured_events(|| {
//!     capture_message("Hello World!", Level::Warning);
//! });
//! assert_eq!(events.len(), 1);
//! assert_eq!(events[0].message.as_ref().unwrap(), "Hello World!");
//! ```

use std::sync::{Arc, Mutex};

use crate::protocol::Event;
use crate::types::Dsn;
use crate::{ClientOptions, Hub, Transport};

lazy_static::lazy_static! {
    static ref TEST_DSN: Dsn = "https://public@sentry.invalid/1".parse().unwrap();
}

/// Collects events instead of sending them.
///
/// # Examples
///
/// ```
/// use sentry::test::TestTransport;
/// use sentry::{ClientOptions, Hub};
/// use std::sync::Arc;
///
/// let transport = TestTransport::new();
/// let options = ClientOptions {
///     dsn: Some("https://public@example.com/1".parse().unwrap()),
///     transport: Some(Arc::new(transport.clone())),
///     ..ClientOptions::default()
/// };
/// Hub::current().bind_client(Some(Arc::new(options.into())));
/// ```
pub struct TestTransport {
    collected: Mutex<Vec<Event<'static>>>,
}

impl TestTransport {
    /// Creates a new test transport.
    #[allow(clippy::new_ret_no_self)]
    pub fn new() -> Arc<TestTransport> {
        Arc::new(TestTransport {
            collected: Mutex::new(vec![]),
        })
    }

    /// Fetches and clears the contained events.
    pub fn fetch_and_clear_events(&self) -> Vec<Event<'static>> {
        let mut guard = self.collected.lock().unwrap();
        std::mem::replace(&mut *guard, vec![])
    }
}

impl Transport for TestTransport {
    fn send_event(&self, event: Event<'static>) {
        self.collected.lock().unwrap().push(event);
    }
}

/// Runs some code with the default test hub and returns the captured events.
///
/// This is a shortcut for creating a testable hub with default options and
/// to call `run_and_capture_events` on it.
pub fn with_captured_events<F: FnOnce()>(f: F) -> Vec<Event<'static>> {
    with_captured_events_options(f, ClientOptions::default())
}

/// Runs some code with the default test hub with the given options and
/// returns the captured events.
///
/// If not DSN is set on the options a default test DSN is inserted.  The
/// transport on the options is also overridden with a `TestTransport`.
///
/// This is a shortcut for creating a testable hub with the supplied options
/// and to call `run_and_capture_events` on it.
pub fn with_captured_events_options<F: FnOnce(), O: Into<ClientOptions>>(
    f: F,
    options: O,
) -> Vec<Event<'static>> {
    let transport = TestTransport::new();
    let mut options = options.into();
    options.dsn = Some(options.dsn.unwrap_or_else(|| TEST_DSN.clone()));
    options.transport = Some(Arc::new(transport.clone()));
    Hub::run(
        Arc::new(Hub::new(
            Some(Arc::new(options.into())),
            Arc::new(Default::default()),
        )),
        f,
    );
    transport.fetch_and_clear_events()
}