use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TraceCorrelation {
pub parent_trace_id: String,
pub parent_span_id: String,
pub child_trace_id: String,
pub child_parent_span_id: String, pub matched: bool,
pub latency_ms: u32,
pub timestamp_correlation_error_ms: u32, }
impl TraceCorrelation {
pub fn is_valid(&self) -> bool {
self.matched
&& self.parent_trace_id == self.child_trace_id
&& self.parent_span_id == self.child_parent_span_id
&& self.latency_ms < 1000 }
}
pub fn correlate_traces(
parent_trace_id: &str,
parent_span_id: &str,
child_trace_id: &str,
child_parent_span_id: &str,
latency_ms: u32,
) -> TraceCorrelation {
let matched = parent_trace_id == child_trace_id && parent_span_id == child_parent_span_id;
TraceCorrelation {
parent_trace_id: parent_trace_id.to_string(),
parent_span_id: parent_span_id.to_string(),
child_trace_id: child_trace_id.to_string(),
child_parent_span_id: child_parent_span_id.to_string(),
matched,
latency_ms,
timestamp_correlation_error_ms: if matched { latency_ms } else { 0 },
}
}
#[derive(Debug, Clone)]
pub struct CorrelationProofSpan {
pub parent_trace_id: String,
pub parent_span_id: String,
pub child_trace_id: String,
pub child_parent_span_id: String,
pub match_result: bool,
pub latency_ms: u32,
pub timestamp_correlation_error_ms: u32,
pub service_name: String,
pub status: String, }
impl CorrelationProofSpan {
pub fn from_correlation(correlation: &TraceCorrelation) -> Self {
CorrelationProofSpan {
parent_trace_id: correlation.parent_trace_id.clone(),
parent_span_id: correlation.parent_span_id.clone(),
child_trace_id: correlation.child_trace_id.clone(),
child_parent_span_id: correlation.child_parent_span_id.clone(),
match_result: correlation.matched,
latency_ms: correlation.latency_ms,
timestamp_correlation_error_ms: correlation.timestamp_correlation_error_ms,
service_name: "wpm".to_string(),
status: if correlation.is_valid() {
"ok".to_string()
} else {
"error".to_string()
},
}
}
pub fn to_otel_attributes(&self) -> HashMap<String, String> {
let mut attrs = HashMap::new();
attrs.insert("parent_trace_id".to_string(), self.parent_trace_id.clone());
attrs.insert("parent_span_id".to_string(), self.parent_span_id.clone());
attrs.insert("child_trace_id".to_string(), self.child_trace_id.clone());
attrs.insert(
"child_parent_span_id".to_string(),
self.child_parent_span_id.clone(),
);
attrs.insert("match".to_string(), self.match_result.to_string());
attrs.insert("latency_ms".to_string(), self.latency_ms.to_string());
attrs.insert(
"timestamp_correlation_error_ms".to_string(),
self.timestamp_correlation_error_ms.to_string(),
);
attrs.insert("service_name".to_string(), self.service_name.clone());
attrs.insert("status".to_string(), self.status.clone());
attrs
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_correlate_matching_traces() {
let correlation = correlate_traces("abc123", "span_001", "abc123", "span_001", 5);
assert!(correlation.matched);
assert!(correlation.is_valid());
}
#[test]
fn test_correlate_mismatched_trace_ids() {
let correlation = correlate_traces("abc123", "span_001", "xyz789", "span_001", 5);
assert!(!correlation.matched);
assert!(!correlation.is_valid());
}
#[test]
fn test_correlate_mismatched_parent_span_ids() {
let correlation = correlate_traces("abc123", "span_001", "abc123", "span_002", 5);
assert!(!correlation.matched);
assert!(!correlation.is_valid());
}
#[test]
fn test_correlate_excessive_latency() {
let correlation = correlate_traces("abc123", "span_001", "abc123", "span_001", 5000);
assert!(correlation.matched);
assert!(!correlation.is_valid(), "Latency >1000ms should fail");
}
#[test]
fn test_correlation_proof_span_from_valid_correlation() {
let correlation = correlate_traces("abc123", "span_001", "abc123", "span_001", 5);
let proof = CorrelationProofSpan::from_correlation(&correlation);
assert_eq!(proof.status, "ok");
assert!(proof.match_result);
}
#[test]
fn test_correlation_proof_span_from_invalid_correlation() {
let correlation = correlate_traces("abc123", "span_001", "xyz789", "span_001", 5);
let proof = CorrelationProofSpan::from_correlation(&correlation);
assert_eq!(proof.status, "error");
assert!(!proof.match_result);
}
#[test]
fn test_correlation_proof_span_to_otel_attributes() {
let correlation = correlate_traces("abc123", "span_001", "abc123", "span_001", 5);
let proof = CorrelationProofSpan::from_correlation(&correlation);
let attrs = proof.to_otel_attributes();
assert_eq!(attrs.get("parent_trace_id").unwrap(), "abc123");
assert_eq!(attrs.get("parent_span_id").unwrap(), "span_001");
assert_eq!(attrs.get("child_trace_id").unwrap(), "abc123");
assert_eq!(attrs.get("match").unwrap(), "true");
assert_eq!(attrs.get("latency_ms").unwrap(), "5");
}
#[test]
fn test_correlation_timestamp_error_valid() {
let correlation = correlate_traces("abc123", "span_001", "abc123", "span_001", 10);
assert_eq!(correlation.timestamp_correlation_error_ms, 10);
}
#[test]
fn test_correlation_timestamp_error_invalid() {
let correlation = correlate_traces("abc123", "span_001", "xyz789", "span_001", 10);
assert_eq!(correlation.timestamp_correlation_error_ms, 0);
}
}