clnrm_core/telemetry/
testing.rs1use opentelemetry::{
7 trace::{Span, Status, Tracer, TracerProvider},
8 KeyValue,
9};
10
11use std::sync::{Arc, Mutex};
12
13use crate::validation::SpanData;
14
15use opentelemetry_sdk::trace::{InMemorySpanExporter, SdkTracerProvider};
16
17pub type TestSpanExporter = InMemorySpanExporter;
19
20pub struct TestTracerProvider {
22 provider: SdkTracerProvider,
23 exporter: TestSpanExporter,
24}
25
26impl Default for TestTracerProvider {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl TestTracerProvider {
33 pub fn new() -> Self {
35 let exporter = TestSpanExporter::default();
36 let processor =
37 opentelemetry_sdk::trace::BatchSpanProcessor::builder(exporter.clone()).build();
38
39 let provider = SdkTracerProvider::builder()
40 .with_span_processor(processor)
41 .build();
42
43 Self { provider, exporter }
44 }
45
46 pub fn tracer(&self) -> opentelemetry_sdk::trace::Tracer {
48 self.provider.tracer("clnrm-test")
49 }
50
51 pub fn exporter(&self) -> &TestSpanExporter {
53 &self.exporter
54 }
55
56 pub fn get_spans(&self) -> Vec<SpanData> {
58 Vec::new()
61 }
62
63 pub fn find_spans_by_name(&self, _name: &str) -> Vec<SpanData> {
65 Vec::new()
68 }
69
70 pub fn find_spans_by_trace_id(&self, _trace_id: &str) -> Vec<SpanData> {
72 Vec::new()
75 }
76
77 pub fn find_spans_by_attribute(&self, _key: &str, _value: &str) -> Vec<SpanData> {
79 Vec::new()
82 }
83
84 pub fn clear(&self) {
86 self.exporter.reset();
87 }
88
89 pub fn has_spans(&self) -> bool {
91 !self
92 .exporter
93 .get_finished_spans()
94 .unwrap_or_default()
95 .is_empty()
96 }
97}
98
99pub struct TestSpanHelper;
101
102impl TestSpanHelper {
103 pub fn create_span(tracer: &opentelemetry_sdk::trace::Tracer, name: &'static str) -> impl Span {
105 tracer.start(name)
106 }
107
108 pub fn create_span_with_attributes(
110 tracer: &opentelemetry_sdk::trace::Tracer,
111 name: &'static str,
112 attributes: Vec<KeyValue>,
113 ) -> impl Span {
114 let mut span = tracer.start(name);
115 for attr in attributes {
116 span.set_attribute(attr);
117 }
118 span
119 }
120
121 pub fn create_span_with_duration(
123 tracer: &opentelemetry_sdk::trace::Tracer,
124 name: &'static str,
125 duration_ms: u64,
126 ) -> impl Span {
127 let mut span = tracer.start(name);
128 span.set_attribute(KeyValue::new("duration_ms", duration_ms as f64));
129 span
130 }
131
132 pub fn create_span_with_status(
134 tracer: &opentelemetry_sdk::trace::Tracer,
135 name: &'static str,
136 status: Status,
137 ) -> impl Span {
138 let mut span = tracer.start(name);
139 span.set_status(status);
140 span
141 }
142
143 pub fn create_parent_child_spans(
145 tracer: &opentelemetry_sdk::trace::Tracer,
146 parent_name: &'static str,
147 child_name: &'static str,
148 ) -> (impl Span, impl Span) {
149 let parent_span = tracer.start(parent_name);
150 let child_span = tracer.start(child_name);
151 (parent_span, child_span)
152 }
153}
154
155pub struct MockOtlpCollector {
157 endpoint: String,
158 received_spans: Arc<Mutex<Vec<crate::validation::SpanData>>>,
159}
160
161impl MockOtlpCollector {
162 pub fn new(endpoint: String) -> Self {
164 Self {
165 endpoint,
166 received_spans: Arc::new(Mutex::new(Vec::new())),
167 }
168 }
169
170 pub fn endpoint(&self) -> &str {
172 &self.endpoint
173 }
174
175 pub fn get_received_spans(&self) -> Vec<crate::validation::SpanData> {
177 self.received_spans
178 .lock()
179 .map(|guard| guard.clone())
180 .unwrap_or_default()
181 }
182
183 pub fn clear(&self) {
185 if let Ok(mut guard) = self.received_spans.lock() {
186 guard.clear();
187 }
188 }
189
190 pub fn has_spans(&self) -> bool {
192 self.received_spans
193 .lock()
194 .map(|guard| !guard.is_empty())
195 .unwrap_or(false)
196 }
197}