use std::collections::{BTreeMap, HashMap};
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone)]
struct SpanFixture {
name: String,
attributes_hashmap: HashMap<String, String>,
attributes_btreemap: BTreeMap<String, String>,
}
impl SpanFixture {
fn new(name: &str) -> Self {
Self {
name: name.to_string(),
attributes_hashmap: HashMap::new(),
attributes_btreemap: BTreeMap::new(),
}
}
fn set_attribute(&mut self, key: &str, value: &str) {
self.attributes_hashmap.insert(key.to_string(), value.to_string());
self.attributes_btreemap.insert(key.to_string(), value.to_string());
}
fn serialize_hashmap_attributes(&self) -> String {
let mut pairs: Vec<String> = self.attributes_hashmap
.iter()
.map(|(k, v)| format!("\"{}\":\"{}\"", k, v))
.collect();
pairs.sort(); format!("{{{}}}", pairs.join(","))
}
fn serialize_btreemap_attributes(&self) -> String {
let pairs: Vec<String> = self.attributes_btreemap
.iter()
.map(|(k, v)| format!("\"{}\":\"{}\"", k, v))
.collect();
format!("{{{}}}", pairs.join(","))
}
}
#[test]
fn test_attribute_order_independence() {
let mut span1 = SpanFixture::new("test_span");
span1.set_attribute("service.name", "checkout");
span1.set_attribute("http.method", "POST");
span1.set_attribute("http.url", "https://api.example.com/v1/orders");
let mut span2 = SpanFixture::new("test_span");
span2.set_attribute("http.url", "https://api.example.com/v1/orders");
span2.set_attribute("service.name", "checkout");
span2.set_attribute("http.method", "POST");
assert_eq!(span1.attributes_hashmap, span2.attributes_hashmap,
"HashMap attributes should be equal regardless of insertion order");
assert_eq!(span1.attributes_btreemap, span2.attributes_btreemap,
"BTreeMap attributes should be equal regardless of insertion order");
assert_eq!(span1.serialize_hashmap_attributes(), span2.serialize_hashmap_attributes(),
"HashMap serialization should be equivalent after sorting");
assert_eq!(span1.serialize_btreemap_attributes(), span2.serialize_btreemap_attributes(),
"BTreeMap serialization should be deterministic and equal");
eprintln!("✅ Attribute order independence verified");
eprintln!("HashMap serialization: {}", span1.serialize_hashmap_attributes());
eprintln!("BTreeMap serialization: {}", span1.serialize_btreemap_attributes());
}
#[test]
fn test_fragile_snapshot_detection() {
let mut span = SpanFixture::new("http.request");
span.set_attribute("service.name", "checkout");
span.set_attribute("http.method", "POST");
span.set_attribute("http.url", "https://api.example.com/v1/orders");
let snapshot_simulation = format!(
r#"{{"name":"{}","attributes":{}}}"#,
span.name,
span.serialize_btreemap_attributes()
);
eprintln!("\n🔍 SNAPSHOT TEST FRAGILITY ANALYSIS");
eprintln!("====================================");
eprintln!("Current snapshot (BTreeMap ordering): {}", snapshot_simulation);
let mut different_order_json = format!(
r#"{{"name":"{}","attributes":{{"http.method":"POST","service.name":"checkout","http.url":"https://api.example.com/v1/orders"}}}}"#,
span.name
);
eprintln!("Possible HashMap order variant: {}", different_order_json);
assert_ne!(snapshot_simulation, different_order_json,
"Different attribute orders produce different JSON - fragile for snapshots!");
eprintln!("\n⚠️ FRAGILITY DETECTED:");
eprintln!(" • Snapshot tests expect specific attribute order");
eprintln!(" • OTLP spec says attributes are unordered");
eprintln!(" • Using BTreeMap enforces alphabetical order (non-spec)");
eprintln!(" • HashMap would be spec-compliant but break snapshots");
}
#[test]
fn test_otlp_spec_compliance() {
eprintln!("\n📋 OTLP ATTRIBUTE ORDER SPECIFICATION");
eprintln!("=====================================");
eprintln!("Per OTLP specification:");
eprintln!(" • Attributes are unordered key-value pairs");
eprintln!(" • No guarantee of order preservation during export");
eprintln!(" • Collectors/receivers must handle any attribute order");
eprintln!(" • Tests should not assume specific ordering");
let mut span_a = SpanFixture::new("test");
span_a.set_attribute("z.last", "value1");
span_a.set_attribute("a.first", "value2");
span_a.set_attribute("m.middle", "value3");
let mut span_b = SpanFixture::new("test");
span_b.set_attribute("a.first", "value2");
span_b.set_attribute("m.middle", "value3");
span_b.set_attribute("z.last", "value1");
assert_eq!(span_a.attributes_hashmap, span_b.attributes_hashmap);
eprintln!("\n✅ SPEC COMPLIANCE VERIFICATION:");
eprintln!(" • Attribute maps are semantically equivalent ✓");
eprintln!(" • Order independence maintained ✓");
eprintln!(" • HashMap usage would be spec-compliant ✓");
eprintln!("\n🚨 CURRENT IMPLEMENTATION CONCERN:");
eprintln!(" • BTreeMap enforces alphabetical ordering");
eprintln!(" • Creates order dependency not required by spec");
eprintln!(" • Snapshot tests rely on this artificial ordering");
}
#[test]
fn test_golden_test_robustness() {
struct TestScenario {
name: &'static str,
attributes: Vec<(&'static str, &'static str)>,
}
let scenarios = vec![
TestScenario {
name: "alphabetical_order",
attributes: vec![
("http.method", "POST"),
("http.url", "https://api.example.com/v1/orders"),
("service.name", "checkout"),
],
},
TestScenario {
name: "reverse_order",
attributes: vec![
("service.name", "checkout"),
("http.url", "https://api.example.com/v1/orders"),
("http.method", "POST"),
],
},
TestScenario {
name: "random_order",
attributes: vec![
("http.url", "https://api.example.com/v1/orders"),
("service.name", "checkout"),
("http.method", "POST"),
],
},
];
eprintln!("\n🧪 GOLDEN TEST ROBUSTNESS ANALYSIS");
eprintln!("==================================");
let mut btree_outputs = Vec::new();
let mut hash_outputs = Vec::new();
for scenario in scenarios {
let mut span = SpanFixture::new("http.request");
for (key, value) in scenario.attributes {
span.set_attribute(key, value);
}
let btree_json = span.serialize_btreemap_attributes();
let hash_json = span.serialize_hashmap_attributes();
btree_outputs.push(btree_json.clone());
hash_outputs.push(hash_json.clone());
eprintln!("Scenario '{}' BTreeMap: {}", scenario.name, btree_json);
}
let btree_consistent = btree_outputs.iter().all(|x| x == &btree_outputs[0]);
assert!(btree_consistent, "BTreeMap should produce consistent ordering");
let hash_consistent = hash_outputs.iter().all(|x| x == &hash_outputs[0]);
assert!(hash_consistent, "HashMap with sorting should be consistent");
eprintln!("\n📊 ROBUSTNESS RESULTS:");
eprintln!(" • BTreeMap consistent: {} ✓", btree_consistent);
eprintln!(" • HashMap (sorted) consistent: {} ✓", hash_consistent);
eprintln!(" • Both approaches can be made deterministic for testing");
eprintln!("\n💡 RECOMMENDATIONS:");
eprintln!(" • Use HashMap for OTLP spec compliance");
eprintln!(" • Sort attributes before snapshot comparison");
eprintln!(" • Focus snapshot tests on attribute content, not order");
}
#[test]
fn demonstrate_snapshot_fix_approach() {
let mut span = SpanFixture::new("http.request");
span.set_attribute("service.name", "checkout");
span.set_attribute("http.method", "POST");
span.set_attribute("http.url", "https://api.example.com/v1/orders");
let fragile_json = format!(
r#"{{"name":"{}","attributes":{}}}"#,
span.name,
span.serialize_btreemap_attributes() );
let robust_json = format!(
r#"{{"name":"{}","attributes":{}}}"#,
span.name,
span.serialize_hashmap_attributes() );
eprintln!("\n🛠️ SNAPSHOT TEST FIX DEMONSTRATION");
eprintln!("===================================");
eprintln!("Fragile approach: {}", fragile_json);
eprintln!("Robust approach: {}", robust_json);
assert_eq!(fragile_json, robust_json, "Normalized outputs should be identical");
eprintln!("\n✅ SOLUTION:");
eprintln!(" • Replace BTreeMap with HashMap in span attributes");
eprintln!(" • Sort attributes during JSON serialization for snapshots");
eprintln!(" • Maintain OTLP spec compliance while preserving test determinism");
}
}