From 0183c94a614e15afe4511509de3b0277b2dcb379 Mon Sep 17 00:00:00 2001 From: Tor Berge Saebjoernsen Date: Thu, 22 Dec 2022 17:03:05 +0100 Subject: [PATCH 1/4] ref: convert integration tests to unit tests about dropping transactions previously there were two integration tests that checked if the transactions got dropped or not when they should based on the sample rate, now there is a single rust unit test that is more targeted to the relevant functions --- relay-server/src/actors/processor.rs | 51 +++++++ relay-server/src/lib.rs | 3 + relay-server/src/testutils.rs | 113 ++++++++++++++ relay-server/src/utils/dynamic_sampling.rs | 162 +++++++-------------- tests/integration/test_dynamic_sampling.py | 69 --------- 5 files changed, 221 insertions(+), 177 deletions(-) create mode 100644 relay-server/src/testutils.rs diff --git a/relay-server/src/actors/processor.rs b/relay-server/src/actors/processor.rs index 1c65c81934..2b695f3179 100644 --- a/relay-server/src/actors/processor.rs +++ b/relay-server/src/actors/processor.rs @@ -2318,7 +2318,12 @@ impl Service for EnvelopeProcessorService { mod tests { use chrono::{DateTime, TimeZone, Utc}; use relay_general::pii::{DataScrubbingConfig, PiiConfig}; + use relay_general::protocol::EventId; + use relay_sampling::{RuleCondition, RuleType, SamplingMode}; + use crate::service::ServiceState; + use crate::testutils::{new_envelope, state_with_rule_and_condition}; + use crate::utils::Semaphore as MySemaphore; use crate::{actors::project::ProjectConfig, extractors::RequestMeta}; use super::*; @@ -2353,6 +2358,52 @@ mod tests { .unwrap() } + #[test] + fn test_it_keeps_or_drops_transactions() { + relay_test::setup(); + let config = Config::from_path("./../.relay").unwrap(); + let arconfig = Arc::new(Config::from_path("./../.relay").unwrap()); + // shouldn't be necessary to start an entire service for a unit test + // it was ported from a python integration test + ServiceState::start(arconfig).unwrap(); + let service = create_test_processor(config); + let envelope = new_envelope(false, "foo"); + + let event = Event { + id: Annotated::new(EventId::new()), + ty: Annotated::new(EventType::Transaction), + transaction: Annotated::new("testing".to_owned()), + ..Event::default() + }; + + for (sample_rate, shouldkeep) in [(0.0, false), (1.0, true)] { + let project_state = state_with_rule_and_condition( + Some(sample_rate), + RuleType::Transaction, + SamplingMode::Received, + RuleCondition::all(), + ); + + let mut state = ProcessEnvelopeState { + envelope: new_envelope(false, "foo"), + event: Annotated::from(event.clone()), + transaction_metrics_extracted: false, + metrics: Default::default(), + sample_rates: None, + extracted_metrics: vec![], + project_state: Arc::new(project_state), + sampling_project_state: None, + project_id: ProjectId::new(42), + envelope_context: EnvelopeContext::new( + &envelope, + MySemaphore::new(42).try_acquire().unwrap(), + ), + }; + let result = service.sample_envelope(&mut state); + assert_eq!(result.is_ok(), shouldkeep); + } + } + #[test] fn test_breadcrumbs_file1() { let item = create_breadcrumbs_item(&[(None, "item1")]); diff --git a/relay-server/src/lib.rs b/relay-server/src/lib.rs index 1dad6ffcc9..7cdc513320 100644 --- a/relay-server/src/lib.rs +++ b/relay-server/src/lib.rs @@ -266,6 +266,9 @@ mod service; mod statsd; mod utils; +#[cfg(test)] +mod testutils; + use relay_config::Config; use relay_system::Controller; diff --git a/relay-server/src/testutils.rs b/relay-server/src/testutils.rs new file mode 100644 index 0000000000..69064e8f26 --- /dev/null +++ b/relay-server/src/testutils.rs @@ -0,0 +1,113 @@ +use bytes::Bytes; +use relay_general::protocol::EventId; +use relay_sampling::{ + DynamicSamplingContext, RuleCondition, RuleId, RuleType, SamplingConfig, SamplingMode, + SamplingRule, +}; + +use crate::actors::project::ProjectState; +use crate::envelope::{Envelope, Item, ItemType}; + +pub fn state_with_rule_and_condition( + sample_rate: Option, + rule_type: RuleType, + mode: SamplingMode, + condition: RuleCondition, +) -> ProjectState { + let rules = match sample_rate { + Some(sample_rate) => vec![SamplingRule { + condition, + sample_rate, + ty: rule_type, + id: RuleId(1), + time_range: Default::default(), + }], + None => Vec::new(), + }; + + state_with_config(SamplingConfig { + rules, + mode, + next_id: None, + }) +} + +pub fn state_with_config(sampling_config: SamplingConfig) -> ProjectState { + let mut state = ProjectState::allowed(); + state.config.dynamic_sampling = Some(sampling_config); + state +} + +pub fn state_with_rule( + sample_rate: Option, + rule_type: RuleType, + mode: SamplingMode, +) -> ProjectState { + let rules = match sample_rate { + Some(sample_rate) => vec![SamplingRule { + condition: RuleCondition::all(), + sample_rate, + ty: rule_type, + id: RuleId(1), + time_range: Default::default(), + }], + None => Vec::new(), + }; + + state_with_config(SamplingConfig { + rules, + mode, + next_id: None, + }) +} +pub fn create_sampling_context(sample_rate: Option) -> DynamicSamplingContext { + DynamicSamplingContext { + trace_id: uuid::Uuid::new_v4(), + public_key: "12345678901234567890123456789012".parse().unwrap(), + release: None, + environment: None, + transaction: None, + sample_rate, + user: Default::default(), + other: Default::default(), + } +} + +/// ugly hack to build an envelope with an optional trace context +pub fn new_envelope>(with_dsc: bool, transaction_name: T) -> Box { + let transaction_name = transaction_name.into(); + let dsn = "https://e12d836b15bb49d7bbf99e64295d995b:@sentry.io/42"; + let event_id = EventId::new(); + + let raw_event = if with_dsc { + format!( + "{{\"transaction\": \"{}\", \"event_id\":\"{}\",\"dsn\":\"{}\", \"trace\": {}}}\n", + transaction_name, + event_id.0.to_simple(), + dsn, + serde_json::to_string(&create_sampling_context(None)).unwrap(), + ) + } else { + format!( + "{{\"transaction\": \"{}\", \"event_id\":\"{}\",\"dsn\":\"{}\"}}\n", + transaction_name, + event_id.0.to_simple(), + dsn, + ) + }; + + let bytes = Bytes::from(raw_event); + + let mut envelope = Envelope::parse_bytes(bytes).unwrap(); + + let item1 = Item::new(ItemType::Transaction); + envelope.add_item(item1); + + let item2 = Item::new(ItemType::Attachment); + envelope.add_item(item2); + + let item3 = Item::new(ItemType::Attachment); + envelope.add_item(item3); + + envelope +} diff --git a/relay-server/src/utils/dynamic_sampling.rs b/relay-server/src/utils/dynamic_sampling.rs index 193a6aa206..f60c689e9f 100644 --- a/relay-server/src/utils/dynamic_sampling.rs +++ b/relay-server/src/utils/dynamic_sampling.rs @@ -166,8 +166,6 @@ pub fn get_sampling_key(envelope: &Envelope) -> Option { #[cfg(test)] mod tests { - use bytes::Bytes; - use relay_common::EventType; use relay_general::protocol::EventId; use relay_general::types::Annotated; @@ -175,115 +173,14 @@ mod tests { EqCondition, RuleCondition, RuleId, RuleType, SamplingConfig, SamplingRule, }; - use crate::envelope::Item; + use crate::testutils::create_sampling_context; + use crate::testutils::new_envelope; + use crate::testutils::state_with_config; + use crate::testutils::state_with_rule; + use crate::testutils::state_with_rule_and_condition; use super::*; - fn state_with_config(sampling_config: SamplingConfig) -> ProjectState { - let mut state = ProjectState::allowed(); - state.config.dynamic_sampling = Some(sampling_config); - state - } - - fn state_with_rule( - sample_rate: Option, - rule_type: RuleType, - mode: SamplingMode, - ) -> ProjectState { - let rules = match sample_rate { - Some(sample_rate) => vec![SamplingRule { - condition: RuleCondition::all(), - sample_rate, - ty: rule_type, - id: RuleId(1), - time_range: Default::default(), - }], - None => Vec::new(), - }; - - state_with_config(SamplingConfig { - rules, - mode, - next_id: None, - }) - } - - fn create_sampling_context(sample_rate: Option) -> DynamicSamplingContext { - DynamicSamplingContext { - trace_id: uuid::Uuid::new_v4(), - public_key: "12345678901234567890123456789012".parse().unwrap(), - release: None, - environment: None, - transaction: None, - sample_rate, - user: Default::default(), - other: Default::default(), - } - } - - /// ugly hack to build an envelope with an optional trace context - fn new_envelope>(with_dsc: bool, transaction_name: T) -> Box { - let transaction_name = transaction_name.into(); - let dsn = "https://e12d836b15bb49d7bbf99e64295d995b:@sentry.io/42"; - let event_id = EventId::new(); - - let raw_event = if with_dsc { - format!( - "{{\"transaction\": \"{}\", \"event_id\":\"{}\",\"dsn\":\"{}\", \"trace\": {}}}\n", - transaction_name, - event_id.0.to_simple(), - dsn, - serde_json::to_string(&create_sampling_context(None)).unwrap(), - ) - } else { - format!( - "{{\"transaction\": \"{}\", \"event_id\":\"{}\",\"dsn\":\"{}\"}}\n", - transaction_name, - event_id.0.to_simple(), - dsn, - ) - }; - - let bytes = Bytes::from(raw_event); - - let mut envelope = Envelope::parse_bytes(bytes).unwrap(); - - let item1 = Item::new(ItemType::Transaction); - envelope.add_item(item1); - - let item2 = Item::new(ItemType::Attachment); - envelope.add_item(item2); - - let item3 = Item::new(ItemType::Attachment); - envelope.add_item(item3); - - envelope - } - - fn state_with_rule_and_condition( - sample_rate: Option, - rule_type: RuleType, - mode: SamplingMode, - condition: RuleCondition, - ) -> ProjectState { - let rules = match sample_rate { - Some(sample_rate) => vec![SamplingRule { - condition, - sample_rate, - ty: rule_type, - id: RuleId(1), - time_range: Default::default(), - }], - None => Vec::new(), - }; - - state_with_config(SamplingConfig { - rules, - mode, - next_id: None, - }) - } - fn samplingresult_from_rules_and_proccessing_flag( rules: Vec, processing_enabled: bool, @@ -660,4 +557,53 @@ mod tests { assert_eq!(spec.unwrap().unwrap().sample_rate, 0.2); } + + #[test] + fn test_sample_rate() { + let event_state_drop = state_with_rule_and_condition( + Some(0.0), + RuleType::Transaction, + SamplingMode::Received, + RuleCondition::all(), + ); + + let envelope = new_envelope(true, "foo"); + + let event = Event { + id: Annotated::new(EventId::new()), + ty: Annotated::new(EventType::Transaction), + transaction: Annotated::new("foo".to_owned()), + ..Event::default() + }; + + // if it matches the transaction rule, the transaction should be dropped + let should_drop = should_keep_event( + envelope.dsc(), + Some(&event), + None, + &event_state_drop, + Some(&event_state_drop), + false, + ); + + assert!(matches!(should_drop, SamplingResult::Drop(_))); + + let event_state_keep = state_with_rule_and_condition( + Some(1.0), + RuleType::Transaction, + SamplingMode::Received, + RuleCondition::all(), + ); + + let should_keep = should_keep_event( + envelope.dsc(), + Some(&event), + None, + &event_state_keep, + Some(&event_state_keep), + false, + ); + + assert!(matches!(should_keep, SamplingResult::Keep)); + } } diff --git a/tests/integration/test_dynamic_sampling.py b/tests/integration/test_dynamic_sampling.py index ccdb83cfc2..9a9f577953 100644 --- a/tests/integration/test_dynamic_sampling.py +++ b/tests/integration/test_dynamic_sampling.py @@ -163,75 +163,6 @@ def _add_trace_info( trace_info["sample_rate"] = json.dumps(client_sample_rate) -def test_it_removes_transactions(mini_sentry, relay): - """ - Tests that when sampling is set to 0% for the trace context project the transactions are removed - """ - project_id = 42 - relay = relay(mini_sentry, _outcomes_enabled_config()) - - # create a basic project config - config = mini_sentry.add_basic_project_config(project_id) - # add a sampling rule to project config that removes all transactions (sample_rate=0) - public_key = config["publicKeys"][0]["publicKey"] - rules = _add_sampling_config(config, sample_rate=0, rule_type="trace") - config["config"]["transactionMetrics"] = {"version": 1} - - # create an envelope with a trace context that is initiated by this project (for simplicity) - envelope = Envelope() - transaction, trace_id, event_id = _create_transaction_item() - envelope.add_transaction(transaction) - _add_trace_info(envelope, trace_id=trace_id, public_key=public_key) - - # send the event, the transaction should be removed. - relay.send_envelope(project_id, envelope) - # the event should be removed by Relay sampling - with pytest.raises(queue.Empty): - mini_sentry.captured_events.get(timeout=1) - - outcomes = mini_sentry.captured_outcomes.get(timeout=2) - assert outcomes is not None - outcome = outcomes["outcomes"][0] - assert outcome.get("outcome") == 1 # filtered - assert outcome.get("category") == 9 # transaction_indexed - assert outcome.get("reason") == f"Sampled:{rules[0]['id']}" - - -def test_it_keeps_transactions(mini_sentry, relay): - """ - Tests that when sampling is set to 100% for the trace context project the transactions are kept - """ - project_id = 42 - relay = relay(mini_sentry, _outcomes_enabled_config()) - - # create a basic project config - config = mini_sentry.add_basic_project_config(project_id) - # add a sampling rule to project config that keeps all transactions (sample_rate=1) - public_key = config["publicKeys"][0]["publicKey"] - _add_sampling_config(config, sample_rate=1, rule_type="trace") - - # create an envelope with a trace context that is initiated by this project (for simplicity) - envelope = Envelope() - transaction, trace_id, event_id = _create_transaction_item() - envelope.add_transaction(transaction) - _add_trace_info(envelope, trace_id=trace_id, public_key=public_key) - - # send the event, the transaction should be removed. - relay.send_envelope(project_id, envelope) - # the event should be left alone by Relay sampling - evt = mini_sentry.captured_events.get(timeout=1).get_transaction_event() - assert evt is not None - # double check that we get back our trace object (check the trace_id from the object) - evt_trace_id = ( - evt.setdefault("contexts", {}).setdefault("trace", {}).get("trace_id") - ) - assert evt_trace_id == trace_id - - # no outcome should be generated since we forward the event to upstream - with pytest.raises(queue.Empty): - mini_sentry.captured_outcomes.get(timeout=2) - - def _create_event_envelope( public_key, client_sample_rate=None, trace_id=None, event_id=None, transaction=None ): From ec3650cb0ef93a06d4175c470fc1c6c3c4eae123 Mon Sep 17 00:00:00 2001 From: Tor Berge Saebjoernsen Date: Thu, 22 Dec 2022 22:04:56 +0100 Subject: [PATCH 2/4] hardcode config value in test --- relay-server/src/actors/processor.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/relay-server/src/actors/processor.rs b/relay-server/src/actors/processor.rs index 2b695f3179..a286b7ca12 100644 --- a/relay-server/src/actors/processor.rs +++ b/relay-server/src/actors/processor.rs @@ -2361,9 +2361,14 @@ mod tests { #[test] fn test_it_keeps_or_drops_transactions() { relay_test::setup(); - let config = Config::from_path("./../.relay").unwrap(); - let arconfig = Arc::new(Config::from_path("./../.relay").unwrap()); - // shouldn't be necessary to start an entire service for a unit test + + // an empty json still produces a valid config + let json_config = serde_json::json!({}); + + let config = Config::from_json_value(json_config.clone()).unwrap(); + let arconfig = Arc::new(Config::from_json_value(json_config).unwrap()); + + // it really shouldn't be necessary to start an entire service for a unit test // it was ported from a python integration test ServiceState::start(arconfig).unwrap(); let service = create_test_processor(config); From 5118f7fcf049b210f10f2d4fa1dc106bdcd4701c Mon Sep 17 00:00:00 2001 From: Tor Berge Saebjoernsen Date: Fri, 13 Jan 2023 10:32:37 +0100 Subject: [PATCH 3/4] rename var --- relay-server/src/actors/processor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/relay-server/src/actors/processor.rs b/relay-server/src/actors/processor.rs index a286b7ca12..03326aa8c5 100644 --- a/relay-server/src/actors/processor.rs +++ b/relay-server/src/actors/processor.rs @@ -2323,7 +2323,7 @@ mod tests { use crate::service::ServiceState; use crate::testutils::{new_envelope, state_with_rule_and_condition}; - use crate::utils::Semaphore as MySemaphore; + use crate::utils::Semaphore as TestSemaphore; use crate::{actors::project::ProjectConfig, extractors::RequestMeta}; use super::*; @@ -2401,7 +2401,7 @@ mod tests { project_id: ProjectId::new(42), envelope_context: EnvelopeContext::new( &envelope, - MySemaphore::new(42).try_acquire().unwrap(), + TestSemaphore::new(42).try_acquire().unwrap(), ), }; let result = service.sample_envelope(&mut state); From 504ec8c783b9d608e65d38889281ff82f89caff1 Mon Sep 17 00:00:00 2001 From: Tor Berge Saebjoernsen Date: Fri, 13 Jan 2023 10:49:36 +0100 Subject: [PATCH 4/4] fix tests --- relay-server/src/utils/dynamic_sampling.rs | 457 ++++++++++----------- 1 file changed, 222 insertions(+), 235 deletions(-) diff --git a/relay-server/src/utils/dynamic_sampling.rs b/relay-server/src/utils/dynamic_sampling.rs index 1cc5e6d3c3..ea9bf93e59 100644 --- a/relay-server/src/utils/dynamic_sampling.rs +++ b/relay-server/src/utils/dynamic_sampling.rs @@ -692,250 +692,237 @@ mod tests { ); assert!(matches!(should_keep, SamplingResult::Keep)); - fn test_event_decaying_rule_with_linear_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Linear { - decayed_sample_rate: 0.2, - }, - Some(now - DateDuration::days(1)), - Some(now + DateDuration::days(1)), - ); - - let sample_rate = - prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) - .unwrap() - .unwrap() - .sample_rate; - let expected_sample_rate = 0.44999999999999996; - - // Workaround against floating point precision differences. - // https://rust-lang.github.io/rust-clippy/master/#float_cmp - assert!((sample_rate - expected_sample_rate).abs() < f64::EPSILON) - } + } + #[test] + fn test_event_decaying_rule_with_linear_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Linear { + decayed_sample_rate: 0.2, + }, + Some(now - DateDuration::days(1)), + Some(now + DateDuration::days(1)), + ); - #[test] - fn test_event_decaying_rule_with_open_time_range_and_linear_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Linear { - decayed_sample_rate: 0.2, - }, - Some(now - DateDuration::days(1)), - None, - ); - - assert!(prepare_and_get_sampling_rule( - 1.0, - EventType::Transaction, - &project_state, - now - ) - .unwrap() - .is_none()); - - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Linear { - decayed_sample_rate: 0.2, - }, - None, - Some(now + DateDuration::days(1)), - ); - - assert!(prepare_and_get_sampling_rule( - 1.0, - EventType::Transaction, - &project_state, - now - ) - .unwrap() - .is_none()); - } + let sample_rate = + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .unwrap() + .sample_rate; + let expected_sample_rate = 0.44999999999999996; - #[test] - fn test_event_decaying_rule_with_no_time_range_and_linear_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Linear { - decayed_sample_rate: 0.2, - }, - None, - None, - ); - - assert!(prepare_and_get_sampling_rule( - 1.0, - EventType::Transaction, - &project_state, - now - ) - .unwrap() - .is_none()); - } + // Workaround against floating point precision differences. + // https://rust-lang.github.io/rust-clippy/master/#float_cmp + assert!((sample_rate - expected_sample_rate).abs() < f64::EPSILON) + } - #[test] - fn test_event_decaying_rule_with_now_equal_start_and_linear_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Linear { - decayed_sample_rate: 0.2, - }, - Some(now), - Some(now + DateDuration::days(1)), - ); - - assert_eq!( - prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) - .unwrap() - .unwrap() - .sample_rate, - 0.7 - ); - } + #[test] + fn test_event_decaying_rule_with_open_time_range_and_linear_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Linear { + decayed_sample_rate: 0.2, + }, + Some(now - DateDuration::days(1)), + None, + ); - #[test] - fn test_event_decaying_rule_with_now_equal_end_and_linear_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Linear { - decayed_sample_rate: 0.2, - }, - Some(now - DateDuration::days(1)), - Some(now), - ); - - assert!(prepare_and_get_sampling_rule( - 1.0, - EventType::Transaction, - &project_state, - now - ) - .unwrap() - .is_none()); - } + assert!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .is_none() + ); - #[test] - fn test_event_decaying_rule_with_base_less_then_decayed_and_linear_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.3), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Linear { - decayed_sample_rate: 0.7, - }, - Some(now - DateDuration::days(1)), - Some(now + DateDuration::days(1)), - ); - - assert!(prepare_and_get_sampling_rule( - 1.0, - EventType::Transaction, - &project_state, - now - ) - .unwrap() - .is_none()); - } + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Linear { + decayed_sample_rate: 0.2, + }, + None, + Some(now + DateDuration::days(1)), + ); - #[test] - fn test_event_decaying_rule_with_constant_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.6), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Constant, - Some(now - DateDuration::days(1)), - Some(now + DateDuration::days(1)), - ); - - assert_eq!( - prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) - .unwrap() - .unwrap() - .sample_rate, - 0.6 - ); - } + assert!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .is_none() + ); + } - #[test] - fn test_event_decaying_rule_with_open_time_range_and_constant_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Constant, - Some(now - DateDuration::days(1)), - None, - ); - - assert_eq!( - prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) - .unwrap() - .unwrap() - .sample_rate, - 0.7 - ); - - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Constant, - None, - Some(now + DateDuration::days(1)), - ); - - assert_eq!( - prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) - .unwrap() - .unwrap() - .sample_rate, - 0.7 - ); - } + #[test] + fn test_event_decaying_rule_with_no_time_range_and_linear_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Linear { + decayed_sample_rate: 0.2, + }, + None, + None, + ); - #[test] - fn test_event_decaying_rule_with_no_time_range_and_constant_function() { - let now = Utc::now(); - let project_state = state_with_decaying_rule( - Some(0.7), - RuleType::Transaction, - SamplingMode::Total, - DecayingFunction::Constant, - None, - None, - ); - - assert_eq!( - prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) - .unwrap() - .unwrap() - .sample_rate, - 0.7 - ); - } + assert!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .is_none() + ); } + + #[test] + fn test_event_decaying_rule_with_now_equal_start_and_linear_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Linear { + decayed_sample_rate: 0.2, + }, + Some(now), + Some(now + DateDuration::days(1)), + ); + + assert_eq!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .unwrap() + .sample_rate, + 0.7 + ); + } + + #[test] + fn test_event_decaying_rule_with_now_equal_end_and_linear_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Linear { + decayed_sample_rate: 0.2, + }, + Some(now - DateDuration::days(1)), + Some(now), + ); + + assert!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .is_none() + ); + } + + #[test] + fn test_event_decaying_rule_with_base_less_then_decayed_and_linear_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.3), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Linear { + decayed_sample_rate: 0.7, + }, + Some(now - DateDuration::days(1)), + Some(now + DateDuration::days(1)), + ); + + assert!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .is_none() + ); + } + + #[test] + fn test_event_decaying_rule_with_constant_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.6), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Constant, + Some(now - DateDuration::days(1)), + Some(now + DateDuration::days(1)), + ); + + assert_eq!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .unwrap() + .sample_rate, + 0.6 + ); + } + + #[test] + fn test_event_decaying_rule_with_open_time_range_and_constant_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Constant, + Some(now - DateDuration::days(1)), + None, + ); + + assert_eq!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .unwrap() + .sample_rate, + 0.7 + ); + + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Constant, + None, + Some(now + DateDuration::days(1)), + ); + + assert_eq!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .unwrap() + .sample_rate, + 0.7 + ); + } + + #[test] + fn test_event_decaying_rule_with_no_time_range_and_constant_function() { + let now = Utc::now(); + let project_state = state_with_decaying_rule( + Some(0.7), + RuleType::Transaction, + SamplingMode::Total, + DecayingFunction::Constant, + None, + None, + ); + + assert_eq!( + prepare_and_get_sampling_rule(1.0, EventType::Transaction, &project_state, now) + .unwrap() + .unwrap() + .sample_rate, + 0.7 + ); + } + #[test] fn test_event_decaying_rule_with_inverse_time_range_and_constant_function() { let now = Utc::now();