use super::TASK_COMPACT_METRIC;use super::emit_compact_metric;use super::emit_turn_memory_metric;use super::emit_turn_network_proxy_metric;use codex_otel::MetricsClient;use codex_otel::MetricsConfig;use codex_otel::SessionTelemetry;use codex_otel::TURN_MEMORY_METRIC;use codex_otel::TURN_NETWORK_PROXY_METRIC;use codex_protocol::ThreadId;use codex_protocol::protocol::SessionSource;use opentelemetry::KeyValue;use opentelemetry_sdk::metrics::InMemoryMetricExporter;use opentelemetry_sdk::metrics::data::AggregatedMetrics;use opentelemetry_sdk::metrics::data::Metric;use opentelemetry_sdk::metrics::data::MetricData;use opentelemetry_sdk::metrics::data::ResourceMetrics;use pretty_assertions::assert_eq;use std::collections::BTreeMap;fn test_session_telemetry() -> SessionTelemetry { let exporter = InMemoryMetricExporter::default(); let metrics = MetricsClient::new( MetricsConfig::in_memory("test", "codex-core", env!("CARGO_PKG_VERSION"), exporter) .with_runtime_reader(), ) .expect("in-memory metrics client"); SessionTelemetry::new( ThreadId::new(), "gpt-5.4", "gpt-5.4", /*account_id*/ None, /*account_email*/ None, /*auth_mode*/ None, "test_originator".to_string(), /*log_user_prompts*/ false, "tty".to_string(), SessionSource::Cli, ) .with_metrics_without_metadata_tags(metrics)}fn find_metric<'a>(resource_metrics: &'a ResourceMetrics, name: &str) -> &'a Metric { for scope_metrics in resource_metrics.scope_metrics() { for metric in scope_metrics.metrics() { if metric.name() == name { return metric; } } } panic!("metric {name} missing");}fn attributes_to_map<'a>( attributes: impl Iterator<Item = &'a KeyValue>,) -> BTreeMap<String, String> { attributes .map(|kv| (kv.key.as_str().to_string(), kv.value.as_str().to_string())) .collect()}fn metric_point(resource_metrics: &ResourceMetrics, name: &str) -> (BTreeMap<String, String>, u64) { let metric = find_metric(resource_metrics, name); match metric.data() { AggregatedMetrics::U64(data) => match data { MetricData::Sum(sum) => { let points: Vec<_> = sum.data_points().collect(); assert_eq!(points.len(), 1); let point = points[0]; (attributes_to_map(point.attributes()), point.value()) } _ => panic!("unexpected counter aggregation"), }, _ => panic!("unexpected counter data type"), }}#[test]fn emit_turn_network_proxy_metric_records_active_turn() { let session_telemetry = test_session_telemetry(); emit_turn_network_proxy_metric( &session_telemetry, /*network_proxy_active*/ true, ("tmp_mem_enabled", "true"), ); let snapshot = session_telemetry .snapshot_metrics() .expect("runtime metrics snapshot"); let (attrs, value) = metric_point(&snapshot, TURN_NETWORK_PROXY_METRIC); assert_eq!(value, 1); assert_eq!( attrs, BTreeMap::from([ ("active".to_string(), "true".to_string()), ("tmp_mem_enabled".to_string(), "true".to_string()), ]) );}#[test]fn emit_turn_network_proxy_metric_records_inactive_turn() { let session_telemetry = test_session_telemetry(); emit_turn_network_proxy_metric( &session_telemetry, /*network_proxy_active*/ false, ("tmp_mem_enabled", "false"), ); let snapshot = session_telemetry .snapshot_metrics() .expect("runtime metrics snapshot"); let (attrs, value) = metric_point(&snapshot, TURN_NETWORK_PROXY_METRIC); assert_eq!(value, 1); assert_eq!( attrs, BTreeMap::from([ ("active".to_string(), "false".to_string()), ("tmp_mem_enabled".to_string(), "false".to_string()), ]) );}#[test]fn emit_turn_memory_metric_records_read_allowed_with_citations() { let session_telemetry = test_session_telemetry(); emit_turn_memory_metric( &session_telemetry, /*feature_enabled*/ true, /*config_enabled*/ true, /*has_citations*/ true, ); let snapshot = session_telemetry .snapshot_metrics() .expect("runtime metrics snapshot"); let (attrs, value) = metric_point(&snapshot, TURN_MEMORY_METRIC); assert_eq!(value, 1); assert_eq!( attrs, BTreeMap::from([ ("config_use_memories".to_string(), "true".to_string()), ("feature_enabled".to_string(), "true".to_string()), ("has_citations".to_string(), "true".to_string()), ("read_allowed".to_string(), "true".to_string()), ]) );}#[test]fn emit_turn_memory_metric_records_config_disabled_without_citations() { let session_telemetry = test_session_telemetry(); emit_turn_memory_metric( &session_telemetry, /*feature_enabled*/ true, /*config_enabled*/ false, /*has_citations*/ false, ); let snapshot = session_telemetry .snapshot_metrics() .expect("runtime metrics snapshot"); let (attrs, value) = metric_point(&snapshot, TURN_MEMORY_METRIC); assert_eq!(value, 1); assert_eq!( attrs, BTreeMap::from([ ("config_use_memories".to_string(), "false".to_string()), ("feature_enabled".to_string(), "true".to_string()), ("has_citations".to_string(), "false".to_string()), ("read_allowed".to_string(), "false".to_string()), ]) );}#[test]fn emit_compact_metric_records_manual_remote_v2() { let session_telemetry = test_session_telemetry(); emit_compact_metric(&session_telemetry, "remote_v2", /*manual*/ true); let snapshot = session_telemetry .snapshot_metrics() .expect("runtime metrics snapshot"); let (attrs, value) = metric_point(&snapshot, TASK_COMPACT_METRIC); assert_eq!(value, 1); assert_eq!( attrs, BTreeMap::from([ ("manual".to_string(), "true".to_string()), ("type".to_string(), "remote_v2".to_string()), ]) );}#[test]fn emit_compact_metric_records_auto_local() { let session_telemetry = test_session_telemetry(); emit_compact_metric(&session_telemetry, "local", /*manual*/ false); let snapshot = session_telemetry .snapshot_metrics() .expect("runtime metrics snapshot"); let (attrs, value) = metric_point(&snapshot, TASK_COMPACT_METRIC); assert_eq!(value, 1); assert_eq!( attrs, BTreeMap::from([ ("manual".to_string(), "false".to_string()), ("type".to_string(), "local".to_string()), ]) );}