telemetry_rust/test/mod.rs
1//! Testing utilities for OpenTelemetry integration testing and validation.
2//!
3//! This module provides utilities for testing OpenTelemetry instrumentation,
4//! including trace header manipulation, Jaeger trace data structures for
5//! validation, and HTTP response testing helpers.
6//!
7//! The module contains tools for:
8//! - Parsing and generating trace headers (traceparent, tracestate)
9//! - Deserializing Jaeger trace data for validation
10//! - Testing HTTP responses with trace context
11//! - Generating test trace IDs and span IDs
12
13pub mod jaegar;
14
15use bytes::Bytes;
16use http_body_util::BodyExt;
17use hyper::{
18 HeaderMap, Response,
19 body::{Body, Incoming},
20 header::HeaderValue,
21};
22
23pub use opentelemetry_api::trace::{SpanId, TraceId};
24use rand::Rng;
25
26/// HTTP response wrapper that includes OpenTelemetry trace information.
27///
28/// This struct wraps an HTTP response and provides easy access to the associated
29/// trace ID and span ID for testing and debugging purposes. It's particularly
30/// useful in integration tests where you need to verify trace propagation.
31///
32/// # Example
33///
34/// ```rust
35/// use telemetry_rust::test::{TracedResponse, Traceparent};
36///
37/// async fn send_traced_request() -> TracedResponse<&'static str> {
38/// let traceparent = Traceparent::generate();
39///
40/// // Send request and get response
41/// let resp = hyper::Response::new("Hello world!");
42///
43/// TracedResponse::new(resp, traceparent)
44/// }
45/// ```
46#[derive(Debug)]
47pub struct TracedResponse<T = Incoming> {
48 resp: Response<T>,
49 /// The OpenTelemetry trace ID associated with this response
50 pub trace_id: TraceId,
51 /// The OpenTelemetry span ID associated with this response
52 pub span_id: SpanId,
53}
54
55impl<T> TracedResponse<T> {
56 /// Creates a new traced response from an HTTP response and trace parent information.
57 ///
58 /// # Arguments
59 ///
60 /// - `resp`: The HTTP response to wrap
61 /// - `traceparent`: The trace parent containing trace and span IDs
62 ///
63 /// # Returns
64 ///
65 /// A new [`TracedResponse`] instance
66 pub fn new(resp: Response<T>, traceparent: Traceparent) -> Self {
67 Self {
68 resp,
69 trace_id: traceparent.trace_id,
70 span_id: traceparent.span_id,
71 }
72 }
73
74 /// Consumes the traced response and returns the inner HTTP response.
75 ///
76 /// # Returns
77 ///
78 /// The wrapped [`hyper::Response`] instance.
79 pub async fn into_inner(self) -> Response<T> {
80 self.resp
81 }
82}
83
84impl<E, T: Body<Data = Bytes, Error = E>> TracedResponse<T> {
85 /// Consumes the response and returns the body as bytes.
86 ///
87 /// # Returns
88 ///
89 /// A future that resolves to the response body as [`bytes::Bytes`]
90 ///
91 /// # Errors
92 ///
93 /// Returns an error if the response body cannot be read
94 pub async fn into_bytes(self) -> Result<Bytes, E> {
95 Ok(self.resp.into_body().collect().await?.to_bytes())
96 }
97}
98
99impl<T> std::ops::Deref for TracedResponse<T> {
100 type Target = Response<T>;
101
102 fn deref(&self) -> &Self::Target {
103 &self.resp
104 }
105}
106
107impl<T> std::ops::DerefMut for TracedResponse<T> {
108 fn deref_mut(&mut self) -> &mut Self::Target {
109 &mut self.resp
110 }
111}
112
113/// Enumeration of supported tracing header formats for testing.
114///
115/// This enum represents the different trace context propagation formats
116/// that can be used in HTTP headers for testing distributed tracing scenarios.
117pub enum TracingHeaderKind {
118 /// W3C Trace Context format using the `traceparent` header
119 Traceparent,
120 /// B3 single header format using the `b3` header
121 B3Single,
122 /// B3 multiple header format using separate `X-B3-*` headers
123 B3Multi,
124}
125
126/// A container for OpenTelemetry trace parent information used in testing.
127///
128/// This struct holds a trace ID and span ID pair that represents a trace context
129/// relationship. It's commonly used for generating test trace headers and
130/// validating trace propagation in integration tests.
131///
132/// # Example
133///
134/// ```rust
135/// use telemetry_rust::test::{Traceparent, TracingHeaderKind};
136///
137/// // Generate a new trace parent for testing
138/// let traceparent = Traceparent::generate();
139///
140/// // Create HTTP headers for trace propagation
141/// let headers = traceparent.get_headers(TracingHeaderKind::Traceparent);
142///
143/// // Use in HTTP request testing
144/// let mut req = hyper::Request::new(());
145/// for (key, value) in headers {
146/// if let Some(header_name) = key {
147/// req.headers_mut().insert(header_name, value);
148/// }
149/// }
150/// ```
151pub struct Traceparent {
152 /// The OpenTelemetry trace ID
153 pub trace_id: TraceId,
154 /// The OpenTelemetry span ID
155 pub span_id: SpanId,
156}
157
158impl Traceparent {
159 /// Generates a new random trace parent with random trace and span IDs.
160 ///
161 /// This method creates a new trace parent with randomly generated IDs,
162 /// useful for creating test scenarios with unique trace contexts.
163 ///
164 /// # Returns
165 ///
166 /// A new [`Traceparent`] with randomly generated trace and span IDs
167 ///
168 /// # Examples
169 ///
170 /// ```rust
171 /// use telemetry_rust::test::Traceparent;
172 ///
173 /// let traceparent = Traceparent::generate();
174 /// println!("Trace ID: {}", traceparent.trace_id);
175 /// ```
176 pub fn generate() -> Self {
177 let mut rng = rand::rng();
178 let trace_id = TraceId::from_u128(rng.random());
179 let span_id = SpanId::from_u64(rng.random());
180 Self { trace_id, span_id }
181 }
182
183 /// Generates HTTP headers containing trace context in the specified format.
184 ///
185 /// This method creates HTTP headers with trace context information formatted
186 /// according to the specified tracing header kind. This is useful for testing
187 /// trace propagation with different header formats.
188 ///
189 /// # Arguments
190 ///
191 /// - `kind`: The format to use for the trace headers
192 ///
193 /// # Returns
194 ///
195 /// A [`HeaderMap`] containing the appropriately formatted trace headers
196 ///
197 /// # Examples
198 ///
199 /// ```rust
200 /// use telemetry_rust::test::{Traceparent, TracingHeaderKind};
201 ///
202 /// let traceparent = Traceparent::generate();
203 /// let headers = traceparent.get_headers(TracingHeaderKind::Traceparent);
204 /// ```
205 ///
206 /// # Panics
207 ///
208 /// This function will panic if the trace ID or span ID cannot be converted
209 /// to a valid HTTP header value format.
210 pub fn get_headers(&self, kind: TracingHeaderKind) -> HeaderMap {
211 let mut map = HeaderMap::new();
212
213 match kind {
214 TracingHeaderKind::Traceparent => {
215 let value = format!("00-{}-{}-01", self.trace_id, self.span_id);
216 map.append("traceparent", HeaderValue::from_str(&value).unwrap());
217 }
218 TracingHeaderKind::B3Single => {
219 let value = format!("{}-{}-1", self.trace_id, self.span_id);
220 map.append("b3", HeaderValue::from_str(&value).unwrap());
221 }
222 TracingHeaderKind::B3Multi => {
223 map.append(
224 "X-B3-TraceId",
225 HeaderValue::from_str(&self.trace_id.to_string()).unwrap(),
226 );
227 map.append(
228 "X-B3-SpanId",
229 HeaderValue::from_str(&self.span_id.to_string()).unwrap(),
230 );
231 map.append("X-B3-Sampled", HeaderValue::from_str("1").unwrap());
232 }
233 }
234
235 map
236 }
237}