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