#![cfg(test)]
#![allow(dead_code)]
use std::collections::VecDeque;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct InMemoryMetricsExporter {
exports: Arc<Mutex<VecDeque<Instant>>>,
export_count: Arc<AtomicUsize>,
}
impl InMemoryMetricsExporter {
fn new() -> Self {
Self {
exports: Arc::new(Mutex::new(VecDeque::new())),
export_count: Arc::new(AtomicUsize::new(0)),
}
}
fn record_export_at(&self, timestamp: Instant) {
self.exports.lock().unwrap().push_back(timestamp);
self.export_count.fetch_add(1, Ordering::Relaxed);
}
fn get_export_intervals(&self) -> Vec<Duration> {
let exports = self.exports.lock().unwrap();
let mut intervals = Vec::new();
for window in exports.iter().collect::<Vec<_>>().windows(2) {
intervals.push(window[1].duration_since(*window[0]));
}
intervals
}
fn export_count(&self) -> usize {
self.export_count.load(Ordering::Relaxed)
}
}
#[derive(Debug, Clone)]
pub struct MetricsCollectionConfigFixture {
collection_interval: Option<Duration>,
timeout: Duration,
export_timeout: Duration,
}
impl MetricsCollectionConfigFixture {
fn new() -> Self {
Self {
collection_interval: None,
timeout: Duration::from_secs(30),
export_timeout: Duration::from_secs(10),
}
}
fn with_collection_interval(mut self, interval: Duration) -> Self {
self.collection_interval = Some(interval);
self
}
fn get_collection_interval(&self) -> Duration {
self.collection_interval.unwrap_or(Duration::from_secs(60)) }
}
#[derive(Debug)]
pub struct DeterministicOtlpMetricsProvider {
config: MetricsCollectionConfigFixture,
exporter: InMemoryMetricsExporter,
}
impl DeterministicOtlpMetricsProvider {
fn new() -> Self {
Self {
config: MetricsCollectionConfigFixture::new(),
exporter: InMemoryMetricsExporter::new(),
}
}
fn with_config(mut self, config: MetricsCollectionConfigFixture) -> Self {
self.config = config;
self
}
fn collection_interval(&self) -> Duration {
self.config.get_collection_interval()
}
fn run_periodic_export_schedule(&self, duration: Duration) -> Vec<Duration> {
let interval = self.collection_interval();
let export_count = (duration.as_millis() / interval.as_millis()) as usize;
let start = Instant::now();
for i in 0..export_count {
let target_time = start + interval * (i as u32 + 1);
self.exporter.record_export_at(target_time);
}
self.exporter.get_export_intervals()
}
}
#[test]
fn audit_otlp_metrics_collection_interval_configuration() {
println!("🔍 AUDIT: OTLP metrics collection interval configuration support");
println!("📋 OTLP metrics collection requirements:");
println!(" • MetricExporter MUST have configurable collection interval");
println!(" • Default interval should be production-appropriate (10-60s)");
println!(" • Applications should configure based on monitoring needs");
println!(" • High-freq: 1-5s, Standard: 10-30s, Low-freq: 60s+");
println!(" • NOT: Force manual PeriodicReader configuration");
let collection_scenarios = vec![
(Duration::from_secs(1), "High-frequency monitoring"),
(Duration::from_secs(5), "Real-time dashboards"),
(Duration::from_millis(500), "Ultra high-frequency"),
(Duration::from_secs(15), "Standard monitoring"),
(Duration::from_secs(30), "Medium-frequency monitoring"),
(Duration::from_secs(60), "Low-frequency monitoring"),
];
println!("📊 Testing collection interval scenarios:");
for (interval, description) in collection_scenarios {
println!(
" Testing: {} ({})",
format_duration(interval),
description
);
let config = MetricsCollectionConfigFixture::new().with_collection_interval(interval);
let provider = DeterministicOtlpMetricsProvider::new().with_config(config);
println!(" Configured interval: {}", format_duration(interval));
println!(
" Provider interval: {}",
format_duration(provider.collection_interval())
);
if provider.collection_interval() == interval {
println!(" ✅ CONFIGURATION: Interval correctly set");
} else {
println!(" ❌ CONFIGURATION: Interval not applied");
}
let test_duration = Duration::from_millis(
u64::try_from(interval.as_millis() * 3).expect("test interval must fit in u64"),
); let actual_intervals = provider.run_periodic_export_schedule(test_duration);
if actual_intervals.is_empty() {
println!(" ⚠️ TIMING: No exports captured in test window");
continue;
}
let expected = interval;
let tolerance = Duration::from_millis(50);
let mut accurate_count = 0;
for actual_interval in &actual_intervals {
let diff = actual_interval.abs_diff(expected);
if diff <= tolerance {
accurate_count += 1;
}
}
let accuracy_ratio = accurate_count as f64 / actual_intervals.len() as f64;
println!(
" Timing accuracy: {:.1}% ({}/{} intervals within tolerance)",
accuracy_ratio * 100.0,
accurate_count,
actual_intervals.len()
);
if accuracy_ratio >= 0.8 {
println!(" ✅ TIMING: Collection interval maintained");
} else {
println!(" ❌ TIMING: Collection interval drift detected");
}
}
}
#[test]
fn audit_current_otlp_metrics_collection_gaps() {
println!("🔍 AUDIT: Current OTLP metrics collection implementation gaps");
println!("📊 Current implementation analysis:");
println!(" File: src/observability/otel.rs");
println!(" Lines 2184, 2225, 2299, etc: PeriodicReader::builder(exporter).build()");
println!(" Issue: No collection interval configuration exposed");
println!("📋 Current configuration limitations:");
println!(" • OtelMetrics::new() - no interval parameter");
println!(" • MetricsConfig - only cardinality/sampling, no collection interval");
println!(" • PeriodicReader configured externally by user");
println!(" • Uses OpenTelemetry SDK default (60s) without override");
let default_config = MetricsCollectionConfigFixture::new(); let default_provider = DeterministicOtlpMetricsProvider::new().with_config(default_config);
println!(
" Default collection interval: {}",
format_duration(default_provider.collection_interval())
);
println!("🚨 CURRENT IMPLEMENTATION GAPS:");
println!(" • No way to configure collection interval through OtelMetrics");
println!(" • Breaks encapsulation - users must manually configure PeriodicReader");
println!(" • Default 60s interval may be too slow for many use cases");
println!(" • No guidance for interval selection in documentation");
println!("📋 REQUIRED IMPROVEMENTS:");
println!(" 1. Add collection_interval field to MetricsConfig");
println!(" 2. Update OtelMetrics to accept interval configuration");
println!(" 3. Provide factory methods for common interval patterns");
println!(" 4. Add with_collection_interval() builder method");
println!(" 5. Document interval selection guidelines");
println!("📊 Use case requirements:");
println!(" • Real-time dashboards: 1-5s intervals");
println!(" • Application monitoring: 10-30s intervals");
println!(" • Infrastructure monitoring: 30-60s intervals");
println!(" • Cost optimization monitoring: 300s+ intervals");
println!("📋 OTLP specification compliance:");
println!(" • OTLP spec requires configurable collection interval: ❌ MISSING");
println!(" • Reasonable default interval (10-60s): ✅ PRESENT (60s)");
println!(" • Application-level configuration: ❌ MISSING");
println!(" • Use-case appropriate intervals: ❌ NOT SUPPORTED");
println!("✅ COLLECTION INTERVAL AUDIT COMPLETE");
println!("🚨 FINDING: Collection interval configuration not exposed to applications");
}
#[test]
fn audit_ideal_collection_interval_patterns() {
println!("🔍 AUDIT: Ideal OTLP metrics collection interval patterns");
println!("📋 Configuration pattern requirements:");
println!(" • Builder pattern for interval configuration");
println!(" • Sensible defaults for different use cases");
println!(" • Runtime interval adjustment capability");
println!(" • Validation of interval ranges");
let configuration_patterns = vec![
(Duration::from_secs(1), "real_time", "Real-time monitoring"),
(
Duration::from_secs(10),
"standard",
"Standard application monitoring",
),
(
Duration::from_secs(30),
"infrastructure",
"Infrastructure monitoring",
),
(
Duration::from_secs(60),
"cost_optimized",
"Cost-optimized monitoring",
),
(Duration::from_secs(300), "batch", "Batch job monitoring"),
];
println!("📊 Testing configuration pattern support:");
for (interval, pattern_name, description) in configuration_patterns {
println!(
" Pattern: {} - {} ({})",
pattern_name,
format_duration(interval),
description
);
let config = MetricsCollectionConfigFixture::new().with_collection_interval(interval);
let provider = DeterministicOtlpMetricsProvider::new().with_config(config);
if provider.collection_interval() == interval {
println!(" ✅ PATTERN: Configuration properly encapsulated");
} else {
println!(" ❌ PATTERN: Configuration not encapsulated");
}
if interval >= Duration::from_millis(100) && interval <= Duration::from_secs(3600) {
println!(" ✅ VALIDATION: Interval within reasonable bounds");
} else {
println!(" ⚠️ VALIDATION: Interval may be problematic");
}
}
println!("📋 Builder pattern requirements:");
println!(" • Fluent configuration API");
println!(" • Chainable configuration methods");
println!(" • Sensible defaults when not configured");
println!(" • Type-safe interval specification");
println!("📋 Factory method patterns for common use cases:");
let factory_patterns = vec![
("MetricsConfig::real_time()", Duration::from_secs(1)),
("MetricsConfig::standard()", Duration::from_secs(15)),
("MetricsConfig::infrastructure()", Duration::from_secs(30)),
("MetricsConfig::cost_optimized()", Duration::from_secs(60)),
];
for (factory_name, expected_interval) in factory_patterns {
println!(
" {}: {} interval",
factory_name,
format_duration(expected_interval)
);
}
println!("✅ COLLECTION INTERVAL PATTERNS AUDIT COMPLETE");
println!("📊 FINDING: Need builder pattern + factory methods for interval config");
}
fn format_duration(duration: Duration) -> String {
let millis = duration.as_millis();
if millis < 1000 {
format!("{}ms", millis)
} else {
format!("{}s", duration.as_secs())
}
}