#![cfg(test)]
use crate::observability::otlp_trace_exporter::{
InMemoryOtlpHttpExporter, LoadSheddingTraceExporter, OtlpSpan, SpanBatch, TraceExporter,
};
use crate::observability::w3c_trace_context::extract_from_http;
use std::collections::HashMap;
use std::time::Duration;
struct HeaderFixtureRequest {
headers: HashMap<String, String>,
}
impl HeaderFixtureRequest {
fn new() -> Self {
Self {
headers: HashMap::new(),
}
}
fn with_traceparent(mut self, traceparent: &str) -> Self {
self.headers
.insert("traceparent".to_string(), traceparent.to_string());
self
}
}
#[test]
fn audit_head_based_sampling_compliance() {
println!("🔍 AUDIT: Head-based sampling compliance per OTLP specification");
let memory_exporter = InMemoryOtlpHttpExporter::new(Duration::from_millis(1));
let exporter = LoadSheddingTraceExporter::new(
Box::new(memory_exporter.clone()),
100,
Duration::from_secs(1),
);
let unsampled_traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00";
let unsampled_request = HeaderFixtureRequest::new().with_traceparent(unsampled_traceparent);
let unsampled_context = extract_from_http(&unsampled_request.headers)
.expect("valid traceparent")
.expect("context present");
assert!(
!unsampled_context.flags.is_sampled(),
"flags=00 should parse as not sampled"
);
let unsampled_batch = SpanBatch {
batch_id: 1,
spans: vec![OtlpSpan::new_with_flags(
unsampled_context.span_id.to_hex(),
"unsampled_operation".to_string(),
1000000000,
1000001000,
vec![("sampled".to_string(), "false".to_string())],
unsampled_context.flags.bits(), )],
created_at: std::time::Instant::now(),
};
let sampled_traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4737-00f067aa0ba902b8-01";
let sampled_request = HeaderFixtureRequest::new().with_traceparent(sampled_traceparent);
let sampled_context = extract_from_http(&sampled_request.headers)
.expect("valid traceparent")
.expect("context present");
assert!(
sampled_context.flags.is_sampled(),
"flags=01 should parse as sampled"
);
let sampled_batch = SpanBatch {
batch_id: 2,
spans: vec![OtlpSpan::new_with_flags(
sampled_context.span_id.to_hex(),
"sampled_operation".to_string(),
1000002000,
1000003000,
vec![("sampled".to_string(), "true".to_string())],
sampled_context.flags.bits(), )],
created_at: std::time::Instant::now(),
};
println!("📋 Submitting unsampled span batch (should be dropped)");
exporter
.export(&unsampled_batch)
.expect("export call should succeed");
println!("📋 Submitting sampled span batch (should be processed)");
exporter
.export(&sampled_batch)
.expect("export call should succeed");
let processed = exporter.process_queue().expect("processing should succeed");
println!("📊 Processed {} batches", processed);
let exported_batches = memory_exporter.exported_batches();
println!(
"📊 Exported {} batches to OTLP endpoint",
exported_batches.len()
);
for (i, batch) in exported_batches.iter().enumerate() {
println!(
" Batch {}: {} spans, batch_id={}",
i,
batch.spans.len(),
batch.batch_id
);
for span in &batch.spans {
println!(" - {} ({})", span.name, span.span_id);
}
}
assert_eq!(
exported_batches.len(),
1,
"OTLP COMPLIANCE VIOLATION: Expected 1 exported batch (only sampled), got {}. \
Head-based sampling MUST drop unsampled spans before serialization.",
exported_batches.len()
);
if !exported_batches.is_empty() {
let exported_batch = &exported_batches[0];
assert_eq!(
exported_batch.batch_id, 2,
"Only the sampled batch (batch_id=2) should be exported"
);
assert_eq!(
exported_batch.spans[0].name, "sampled_operation",
"Only sampled spans should reach OTLP export"
);
}
println!("✅ HEAD-BASED SAMPLING COMPLIANCE VERIFIED");
println!(" ✓ Unsampled spans (traceflags=0) dropped before export");
println!(" ✓ Sampled spans (traceflags=1) exported to OTLP endpoint");
}
#[test]
fn audit_current_implementation_drops_unsampled_spans() {
println!("AUDIT: Verifying current head-based sampling implementation");
let memory_exporter = InMemoryOtlpHttpExporter::new(Duration::from_millis(1));
let exporter = LoadSheddingTraceExporter::new(
Box::new(memory_exporter.clone()),
100,
Duration::from_secs(1),
);
let unsampled_batch = SpanBatch {
batch_id: 1,
spans: vec![OtlpSpan::new_with_flags(
"unsampled123456789".to_string(),
"unsampled_operation".to_string(),
1000000000,
1000001000,
vec![("trace_flags".to_string(), "0".to_string())],
0x00, )],
created_at: std::time::Instant::now(),
};
exporter
.export(&unsampled_batch)
.expect("export should succeed");
exporter.process_queue().expect("processing should succeed");
let exported_batches = memory_exporter.exported_batches();
assert!(
exported_batches.is_empty(),
"unsampled spans must be dropped before OTLP export; exported {} batches",
exported_batches.len()
);
println!("Head-based sampling implementation drops unsampled spans");
}
#[test]
fn audit_mixed_sampling_batch_filtering() {
println!("🔍 AUDIT: Mixed sampling batch filtering");
let memory_exporter = InMemoryOtlpHttpExporter::new(Duration::from_millis(1));
let exporter = LoadSheddingTraceExporter::new(
Box::new(memory_exporter.clone()),
100,
Duration::from_secs(1),
);
let mixed_batch = SpanBatch {
batch_id: 1,
spans: vec![
OtlpSpan::new_with_flags(
"unsampled_span_1".to_string(),
"unsampled_op_1".to_string(),
1000000000,
1000001000,
vec![("trace_flags".to_string(), "0".to_string())],
0x00, ),
OtlpSpan::new_with_flags(
"sampled_span_1".to_string(),
"sampled_op_1".to_string(),
1000002000,
1000003000,
vec![("trace_flags".to_string(), "1".to_string())],
0x01, ),
OtlpSpan::new_with_flags(
"unsampled_span_2".to_string(),
"unsampled_op_2".to_string(),
1000004000,
1000005000,
vec![("trace_flags".to_string(), "0".to_string())],
0x00, ),
],
created_at: std::time::Instant::now(),
};
exporter
.export(&mixed_batch)
.expect("export should succeed");
exporter.process_queue().expect("processing should succeed");
let exported_batches = memory_exporter.exported_batches();
if !exported_batches.is_empty() {
let exported_batch = &exported_batches[0];
assert_eq!(
exported_batch.spans.len(),
1,
"Only 1 sampled span should be exported from mixed batch"
);
assert_eq!(
exported_batch.spans[0].name, "sampled_op_1",
"Only the sampled span should remain after filtering"
);
}
println!("✅ MIXED BATCH FILTERING VERIFIED");
}