Skip to main content

provide_telemetry/
testing.rs

1// SPDX-FileCopyrightText: Copyright (C) 2026 provide.io llc
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-Comment: Part of provide-telemetry.
4//
5
6use crate::backpressure::_reset_backpressure_for_tests;
7use crate::cardinality::clear_cardinality_limits;
8use crate::classification::clear_classification_rules;
9use crate::consent::reset_consent_for_tests;
10use crate::context::{reset_context_for_tests, reset_trace_context_for_tests};
11use crate::health::_reset_health_for_tests;
12use crate::metrics::reset_metrics_for_tests;
13use crate::otel::_reset_otel_for_tests;
14use crate::pii::{replace_pii_rules, reset_secret_patterns_for_tests};
15use crate::receipts::reset_receipts_for_tests;
16use crate::resilience::_reset_resilience_for_tests;
17use crate::sampling::_reset_sampling_for_tests;
18use crate::schema::_reset_schema_for_tests;
19use crate::setup::shutdown_telemetry;
20use crate::slo::reset_slo_for_tests;
21use crate::tracer::set_trace_context;
22use std::sync::{Mutex, MutexGuard, OnceLock};
23
24/// Observe this SDK's real config defaults and applicability, variable by
25/// variable. Lives here rather than in a probe binary so the executable
26/// contract test and the cross-language parity gate measure the same thing.
27pub use crate::config::probe::{config_defaults_probe, ProbedConfigEntry};
28
29static TEST_STATE_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
30
31#[cfg_attr(test, mutants::skip)] // Equivalent mutants only swap in Mutex::default().
32fn default_test_state_lock_mutex() -> Mutex<()> {
33    Mutex::new(())
34}
35
36fn test_state_lock() -> &'static Mutex<()> {
37    TEST_STATE_LOCK.get_or_init(default_test_state_lock_mutex)
38}
39
40/// Serialize tests that mutate process-global telemetry state.
41pub fn acquire_test_state_lock() -> MutexGuard<'static, ()> {
42    test_state_lock().lock().unwrap_or_else(|e| e.into_inner())
43}
44
45/// Reset all telemetry state to keep tests isolated.
46pub fn reset_telemetry_state() {
47    let _ = shutdown_telemetry(None);
48    _reset_otel_for_tests();
49    // Restore the stranded-drain-worker budget. Sound even with workers still
50    // pending: each decrements with a saturating subtraction on its way out.
51    #[cfg(feature = "otel")]
52    crate::otel::_reset_abandoned_workers_for_tests();
53    _reset_health_for_tests();
54    _reset_backpressure_for_tests();
55    _reset_sampling_for_tests();
56    _reset_resilience_for_tests();
57    reset_metrics_for_tests();
58    reset_receipts_for_tests();
59    reset_consent_for_tests();
60    reset_slo_for_tests();
61    clear_cardinality_limits();
62    clear_classification_rules();
63    replace_pii_rules(Vec::new());
64    reset_secret_patterns_for_tests();
65    _reset_schema_for_tests();
66    reset_context_for_tests();
67}
68
69/// Clear manual trace context to prevent cross-test leakage.
70pub fn reset_trace_context() {
71    reset_trace_context_for_tests();
72    drop(set_trace_context(None, None));
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn testing_test_acquire_lock_recovers_after_poison() {
81        let handle = std::thread::spawn(|| {
82            let _guard = test_state_lock()
83                .lock()
84                .expect("test state lock should acquire before poison");
85            panic!("poison test state lock");
86        });
87        assert!(handle.join().is_err(), "worker must panic to poison lock");
88
89        let _guard = acquire_test_state_lock();
90    }
91}