diff --git a/libdd-data-pipeline/src/agent_info/fetcher.rs b/libdd-data-pipeline/src/agent_info/fetcher.rs index 131e4c04fc..3fdbdf7026 100644 --- a/libdd-data-pipeline/src/agent_info/fetcher.rs +++ b/libdd-data-pipeline/src/agent_info/fetcher.rs @@ -88,7 +88,7 @@ pub async fn fetch_info_with_state( /// let agent_info = libdd_data_pipeline::agent_info::fetch_info::(&endpoint) /// .await /// .unwrap(); -/// println!("Agent version is {}", agent_info.info.version.unwrap()); +/// println!("Agent version is {:?}", agent_info.info.version); /// # Ok(()) /// # } /// ``` @@ -180,10 +180,7 @@ async fn fetch_and_hash_response( /// /// // Access the info /// if let Some(agent_info) = agent_info_arc.as_ref() { -/// println!( -/// "Agent version is {}", -/// agent_info.info.version.as_ref().unwrap() -/// ); +/// println!("Agent version is {:?}", agent_info.info.version); /// } /// # Ok(()) /// # } @@ -366,6 +363,7 @@ impl ResponseObserver { mod single_threaded_tests { use super::*; use crate::agent_info; + use crate::agent_info::schema::AgentVersion; use httpmock::prelude::*; use libdd_capabilities_impl::NativeCapabilities; use libdd_shared_runtime::SharedRuntime; @@ -600,7 +598,7 @@ mod single_threaded_tests { when.path("/info"); then.status(200) .header("content-type", "application/json") - .body(r#"{"version":"1"}"#); + .body(r#"{"version":"7.65.0"}"#); }); let endpoint = Endpoint::from_url(server.url("/info").parse().unwrap()); let (fetcher, _response_observer) = AgentInfoFetcher::::new( @@ -626,9 +624,16 @@ mod single_threaded_tests { .unwrap() .info .version - .clone() - .unwrap(); - assert_eq!(version_1, "1"); + .clone(); + assert_eq!( + version_1, + Some(AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: None, + }) + ); // Update the info endpoint mock_v1.delete(); @@ -636,7 +641,7 @@ mod single_threaded_tests { when.path("/info"); then.status(200) .header("content-type", "application/json") - .body(r#"{"version":"2"}"#); + .body(r#"{"version":"7.66.0-rc.1+linux"}"#); }); // Wait for second fetch @@ -659,10 +664,17 @@ mod single_threaded_tests { .unwrap() .info .version - .clone() - .unwrap(); + .clone(); if version_2 != version_1 { - assert_eq!(version_2, "2"); + assert_eq!( + version_2, + Some(AgentVersion { + major: 7, + minor: 66, + patch: 0, + metadata: Some("-rc.1+linux".to_string()), + }) + ); break; } std::thread::sleep(Duration::from_millis(100)); @@ -677,13 +689,13 @@ mod single_threaded_tests { when.path("/info"); then.status(200) .header("content-type", "application/json") - .body(r#"{"version":"triggered"}"#); + .body(r#"{"version":"7.66.0"}"#); }); // Populate the cache with initial state AGENT_INFO_CACHE.store(Some(Arc::new(AgentInfo { state_hash: "old_state".to_string(), - info: serde_json::from_str(r#"{"version":"old"}"#).unwrap(), + info: serde_json::from_str(r#"{"version":"7.65.0"}"#).unwrap(), }))); let endpoint = Endpoint::from_url(server.url("/info").parse().unwrap()); @@ -719,7 +731,7 @@ mod single_threaded_tests { // Wait for the cache to be updated with proper timeout let mut attempts = 0; - let expected_hash = calculate_hash(r#"{"version":"triggered"}"#); + let expected_hash = calculate_hash(r#"{"version":"7.66.0"}"#); while attempts < MAX_ATTEMPTS { let updated_info = AGENT_INFO_CACHE.load(); @@ -737,14 +749,13 @@ mod single_threaded_tests { assert!(updated_info.is_some()); assert_eq!(updated_info.as_ref().unwrap().state_hash, expected_hash); assert_eq!( - updated_info - .as_ref() - .unwrap() - .info - .version - .as_ref() - .unwrap(), - "triggered" + updated_info.as_ref().unwrap().info.version, + Some(AgentVersion { + major: 7, + minor: 66, + patch: 0, + metadata: None, + }) ); } @@ -756,10 +767,10 @@ mod single_threaded_tests { when.path("/info"); then.status(200) .header("content-type", "application/json") - .body(r#"{"version":"same"}"#); + .body(r#"{"version":"7.65.0"}"#); }); - let same_json = r#"{"version":"same"}"#; + let same_json = r#"{"version":"7.65.0"}"#; let same_hash = calculate_hash(same_json); // Populate the cache with the same state diff --git a/libdd-data-pipeline/src/agent_info/schema.rs b/libdd-data-pipeline/src/agent_info/schema.rs index 7b7bfc4e3a..081cf1c3f3 100644 --- a/libdd-data-pipeline/src/agent_info/schema.rs +++ b/libdd-data-pipeline/src/agent_info/schema.rs @@ -1,8 +1,111 @@ // Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/ // SPDX-License-Identifier: Apache-2.0 //! This module provides struct representing the info endpoint response -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::collections::HashMap; +use std::fmt; +use tracing::debug; + +/// Parsed agent version. +/// +/// `metadata` captures the pre-release and/or build suffix exactly as it +/// appeared in the original string, including the leading separator (`-` or +/// `+`). For example `"7.65.0-rc.1+abc"` parses to +/// `AgentVersion { major: 7, minor: 65, patch: 0, metadata: Some("-rc.1+abc") }`, +/// and `"7.65.0"` parses to `metadata: None`. This makes the original string +/// losslessly recoverable via the [`fmt::Display`] impl. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct AgentVersion { + /// Major version component. + pub major: u32, + /// Minor version component. + pub minor: u32, + /// Patch version component. + pub patch: u32, + /// Pre-release and/or build metadata, including the leading `-` or `+`. + pub metadata: Option, +} + +impl AgentVersion { + /// Return true if the version is greater than or equal to the given minimum version + pub fn check_minimum_version(&self, minimum_version: (u32, u32, u32)) -> bool { + (self.major, self.minor, self.patch) > minimum_version + || ((self.major, self.minor, self.patch) == minimum_version + // If the agent has the minimum version, check that it's not a pre-release + && self.metadata + .as_ref() + .is_none_or(|meta| !meta.starts_with("-"))) + } +} + +impl fmt::Display for AgentVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?; + if let Some(metadata) = &self.metadata { + f.write_str(metadata)?; + } + Ok(()) + } +} + +/// Deserialize the agent `version` JSON string into an [`AgentVersion`]. +/// +/// Accepts strings of the form `MAJOR.MINOR.PATCH` with an optional +/// pre-release/build suffix (e.g. `"7.65.0-rc.1+abc"`). Returns `None` if the +/// field is absent, null, or cannot be parsed. A non-null but unparseable +/// value is logged at `debug` so operators have a breadcrumb when CSS gating +/// unexpectedly stays off. +fn deserialize_agent_version<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let Some(raw) = Option::::deserialize(deserializer)? else { + return Ok(None); + }; + + fn parse_inner(raw: &str) -> Option { + let mut parts = raw.splitn(3, '.'); + let major = parts.next()?.parse().ok()?; + let minor = parts.next()?.parse().ok()?; + let tail = parts.next()?; + let (patch_str, metadata) = match tail.find(['-', '+']) { + Some(idx) => (&tail[..idx], Some(tail[idx..].to_string())), + None => (tail, None), + }; + let patch = patch_str.parse().ok()?; + Some(AgentVersion { + major, + minor, + patch, + metadata, + }) + } + let parsed = parse_inner(&raw); + if parsed.is_none() { + debug!( + raw = %raw, + "failed to parse agent version, expected MAJOR.MINOR.PATCH[]", + ); + } + Ok(parsed) +} + +/// Serialize a parsed agent version back to its canonical +/// `MAJOR.MINOR.PATCH[]` string form. Uses [`Serializer::collect_str`] +/// to stream the [`fmt::Display`] output directly into the serializer without +/// an intermediate [`String`] allocation. +fn serialize_agent_version( + version: &Option, + serializer: S, +) -> Result +where + S: Serializer, +{ + match version { + Some(v) => serializer.collect_str(v), + None => serializer.serialize_none(), + } +} /// Wrapper for an agent info response storing the state hash from the agent #[derive(Clone, Deserialize, Default, Debug, PartialEq)] @@ -17,8 +120,15 @@ pub struct AgentInfo { #[allow(missing_docs)] #[derive(Clone, Serialize, Deserialize, Default, Debug, PartialEq)] pub struct AgentInfoStruct { - /// Version of the agent - pub version: Option, + /// Version of the agent, parsed from the `MAJOR.MINOR.PATCH[]` + /// JSON string into an [`AgentVersion`]. `None` if the field is absent or + /// unparseable. + #[serde( + default, + deserialize_with = "deserialize_agent_version", + serialize_with = "serialize_agent_version" + )] + pub version: Option, /// Commit of the version of the agent pub git_commit: Option, /// List of available endpoints @@ -119,7 +229,7 @@ pub struct MemcachedObfuscationConfig { #[cfg(test)] mod tests { - use super::AgentInfoStruct; + use super::{AgentInfoStruct, AgentVersion}; #[duplicate::duplicate_item( test_name input; [parse_empty] ["{}"]; @@ -196,4 +306,227 @@ mod tests { let _info: AgentInfoStruct = serde_json::from_str(input) .expect("AgentInfoStruct should be parsed successfully from input"); } + + #[test] + fn version_is_parsed_into_struct() { + for (input, expected) in [ + ( + r#"{"version":"7.65.0"}"#, + Some(AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: None, + }), + ), + ( + r#"{"version":"7.65.1"}"#, + Some(AgentVersion { + major: 7, + minor: 65, + patch: 1, + metadata: None, + }), + ), + ( + r#"{"version":"10.0.123"}"#, + Some(AgentVersion { + major: 10, + minor: 0, + patch: 123, + metadata: None, + }), + ), + ] { + let info: AgentInfoStruct = serde_json::from_str(input).unwrap(); + assert_eq!(info.version, expected, "input: {input}"); + } + } + + #[test] + fn version_captures_pre_release_and_build_metadata() { + for (input, expected_metadata) in [ + (r#"{"version":"7.65.0-rc.1"}"#, "-rc.1"), + (r#"{"version":"7.65.0+abc"}"#, "+abc"), + (r#"{"version":"7.65.0-rc.1+abc"}"#, "-rc.1+abc"), + ] { + let info: AgentInfoStruct = serde_json::from_str(input).unwrap(); + assert_eq!( + info.version, + Some(AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: Some(expected_metadata.to_string()), + }), + "input: {input}", + ); + } + } + + #[test] + fn unparseable_version_becomes_none() { + for input in [ + r#"{"version":"1"}"#, + r#"{"version":"7"}"#, + r#"{"version":"7.65"}"#, + r#"{"version":"7.65.0.1"}"#, + r#"{"version":""}"#, + r#"{"version":"unknown"}"#, + r#"{"version":"a.b.c"}"#, + r#"{"version":null}"#, + ] { + let info: AgentInfoStruct = serde_json::from_str(input).unwrap(); + assert_eq!(info.version, None, "input: {input}"); + } + } + + #[test] + fn check_minimum_version_exact_match_no_metadata() { + // Exact match with no metadata should pass (stable release) + let v = AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: None, + }; + assert!(v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_exact_match_build_metadata() { + // Exact match with build metadata (`+`) is not a pre-release, so it should pass + let v = AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: Some("+build.42".to_string()), + }; + assert!(v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_exact_match_prerelease_fails() { + // Exact match but the version is a pre-release — should NOT satisfy the minimum + let v = AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: Some("-rc.1".to_string()), + }; + assert!(!v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_exact_match_prerelease_plus_build_fails() { + // Pre-release with build metadata attached — still a pre-release, should fail + let v = AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: Some("-rc.1+abc".to_string()), + }; + assert!(!v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_greater_patch() { + let v = AgentVersion { + major: 7, + minor: 65, + patch: 1, + metadata: None, + }; + assert!(v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_greater_patch_with_prerelease() { + // Higher patch trumps pre-release status — should still pass + let v = AgentVersion { + major: 7, + minor: 65, + patch: 1, + metadata: Some("-rc.1".to_string()), + }; + assert!(v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_greater_minor() { + let v = AgentVersion { + major: 7, + minor: 66, + patch: 0, + metadata: None, + }; + assert!(v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_greater_major() { + let v = AgentVersion { + major: 8, + minor: 0, + patch: 0, + metadata: None, + }; + assert!(v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_lesser_patch() { + let v = AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: None, + }; + assert!(!v.check_minimum_version((7, 65, 1))); + } + + #[test] + fn check_minimum_version_lesser_minor() { + let v = AgentVersion { + major: 7, + minor: 64, + patch: 99, + metadata: None, + }; + assert!(!v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn check_minimum_version_lesser_major() { + let v = AgentVersion { + major: 6, + minor: 99, + patch: 99, + metadata: None, + }; + assert!(!v.check_minimum_version((7, 65, 0))); + } + + #[test] + fn missing_version_field_is_none() { + let info: AgentInfoStruct = serde_json::from_str("{}").unwrap(); + assert_eq!(info.version, None); + } + + #[test] + fn version_round_trips_through_json() { + for input in [ + r#"{"version":"7.65.1"}"#, + r#"{"version":"7.65.0-rc.1"}"#, + r#"{"version":"7.65.0+abc"}"#, + r#"{"version":"7.65.0-rc.1+abc"}"#, + ] { + let info: AgentInfoStruct = serde_json::from_str(input).unwrap(); + let json = serde_json::to_string(&info).unwrap(); + assert!( + json.contains(input.trim_matches(|c| c == '{' || c == '}')), + "input: {input}, got: {json}" + ); + } + } } diff --git a/libdd-data-pipeline/src/trace_exporter/mod.rs b/libdd-data-pipeline/src/trace_exporter/mod.rs index 7e3dd1c951..05a09bdcbc 100644 --- a/libdd-data-pipeline/src/trace_exporter/mod.rs +++ b/libdd-data-pipeline/src/trace_exporter/mod.rs @@ -1888,7 +1888,7 @@ mod single_threaded_tests { then.status(200) .header("content-type", "application/json") .header("datadog-agent-state", "1") - .body(r#"{"version":"1","client_drop_p0s":true,"endpoints":["/v0.4/traces","/v0.6/stats"]}"#); + .body(r#"{"version":"7.65.0","client_drop_p0s":true,"endpoints":["/v0.4/traces","/v0.6/stats"]}"#); }); let runtime = Arc::new(SharedRuntime::new().unwrap()); @@ -1983,7 +1983,7 @@ mod single_threaded_tests { then.status(200) .header("content-type", "application/json") .header("datadog-agent-state", "1") - .body(r#"{"version":"1","client_drop_p0s":true,"endpoints":["/v0.4/traces","/v0.6/stats"]}"#); + .body(r#"{"version":"7.65.0","client_drop_p0s":true,"endpoints":["/v0.4/traces","/v0.6/stats"]}"#); }); let runtime = Arc::new(SharedRuntime::new().unwrap()); @@ -2086,9 +2086,9 @@ mod single_threaded_tests { let info_body = match agent_obfuscation_version { Some(v) => format!( - r#"{{"version":"1","client_drop_p0s":true,"obfuscation_version":{v},"endpoints":["/v0.4/traces","/v0.6/stats"]}}"# + r#"{{"version":"7.65.0","client_drop_p0s":true,"obfuscation_version":{v},"endpoints":["/v0.4/traces","/v0.6/stats"]}}"# ), - None => r#"{"version":"1","client_drop_p0s":true,"endpoints":["/v0.4/traces","/v0.6/stats"]}"#.to_string(), + None => r#"{"version":"7.65.0","client_drop_p0s":true,"endpoints":["/v0.4/traces","/v0.6/stats"]}"#.to_string(), }; let _mock_info = server.mock(|when, then| { when.method(GET).path("/info"); diff --git a/libdd-data-pipeline/src/trace_exporter/stats.rs b/libdd-data-pipeline/src/trace_exporter/stats.rs index 7f1aaea36a..adea9463ab 100644 --- a/libdd-data-pipeline/src/trace_exporter/stats.rs +++ b/libdd-data-pipeline/src/trace_exporter/stats.rs @@ -79,6 +79,31 @@ pub(crate) struct StatsComputationConfig { pub(crate) obfuscation_enabled: bool, } +/// Minimum agent version required to support client-side stats computation. +#[cfg(not(target_arch = "wasm32"))] +const MIN_STATS_AGENT_VERSION: (u32, u32, u32) = (7, 65, 0); + +/// Return true if the agent supports client-side stats. +/// +/// This requires: +/// - `client_drop_p0s` to be enabled on the agent, +/// - the agent version to be >= 7.65.0, and +/// - the `/v0.6/stats` endpoint to be advertised by the agent. +#[cfg(not(target_arch = "wasm32"))] +fn is_stats_computation_supported(agent_info: &AgentInfo) -> bool { + agent_info.info.client_drop_p0s.is_some_and(|v| v) + && agent_info + .info + .version + .as_ref() + .is_some_and(|v| v.check_minimum_version(MIN_STATS_AGENT_VERSION)) + && agent_info + .info + .endpoints + .as_ref() + .is_some_and(|endpoints| endpoints.iter().any(|e| e == STATS_ENDPOINT)) +} + /// Return true if the agent's obfuscation version is supported by this tracer #[cfg(feature = "stats-obfuscation")] fn is_obfuscation_active(agent_info: &AgentInfo) -> bool { @@ -201,7 +226,7 @@ pub(crate) fn handle_stats_disabled_by_agent< capabilities: C, client_side_stats: &StatsComputationConfig, ) { - if agent_info.info.client_drop_p0s.is_some_and(|v| v) { + if is_stats_computation_supported(agent_info) { let status = start_stats_computation( ctx, get_span_kinds_for_stats(agent_info), @@ -261,7 +286,7 @@ pub(crate) fn handle_stats_enabled( stats_concentrator: &Arc>, client_side_stats: &StatsComputationConfig, ) { - if agent_info.info.client_drop_p0s.is_some_and(|v| v) { + if is_stats_computation_supported(agent_info) { let mut concentrator = stats_concentrator.lock_or_panic(); concentrator.set_span_kinds(get_span_kinds_for_stats(agent_info)); concentrator.set_peer_tags(agent_info.info.peer_tags.clone().unwrap_or_default()); @@ -353,4 +378,82 @@ mod tests { SUPPORTED_OBFUSCATION_VERSION_STR ); } + + #[cfg(not(target_arch = "wasm32"))] + mod is_stats_computation_supported { + use crate::agent_info::schema::{AgentInfo, AgentInfoStruct}; + use crate::trace_exporter::stats::{is_stats_computation_supported, STATS_ENDPOINT}; + + fn make_agent_info( + client_drop_p0s: Option, + version: Option<&str>, + endpoints: Option>, + ) -> AgentInfo { + // Route the version string through deserialization so the tuple is + // computed by the same code path as the real fetcher. + let version = version.and_then(|v| { + let info: AgentInfoStruct = + serde_json::from_str(&format!(r#"{{"version":"{v}"}}"#)).unwrap(); + info.version + }); + AgentInfo { + state_hash: String::new(), + info: AgentInfoStruct { + client_drop_p0s, + version, + endpoints: endpoints.map(|e| e.into_iter().map(String::from).collect()), + ..Default::default() + }, + } + } + + #[test] + fn supported_when_all_requirements_met() { + let info = make_agent_info( + Some(true), + Some("7.65.0"), + Some(vec!["/v0.4/traces", STATS_ENDPOINT]), + ); + assert!(is_stats_computation_supported(&info)); + } + + #[test] + fn supported_for_newer_version() { + let info = make_agent_info(Some(true), Some("7.80.3"), Some(vec![STATS_ENDPOINT])); + assert!(is_stats_computation_supported(&info)); + } + + #[test] + fn unsupported_when_client_drop_p0s_missing_or_false() { + let info = make_agent_info(None, Some("7.65.0"), Some(vec![STATS_ENDPOINT])); + assert!(!is_stats_computation_supported(&info)); + + let info = make_agent_info(Some(false), Some("7.65.0"), Some(vec![STATS_ENDPOINT])); + assert!(!is_stats_computation_supported(&info)); + } + + #[test] + fn unsupported_when_version_too_old() { + let info = make_agent_info(Some(true), Some("7.64.99"), Some(vec![STATS_ENDPOINT])); + assert!(!is_stats_computation_supported(&info)); + } + + #[test] + fn unsupported_when_version_missing_or_unparseable() { + let info = make_agent_info(Some(true), None, Some(vec![STATS_ENDPOINT])); + assert!(!is_stats_computation_supported(&info)); + + let info = make_agent_info(Some(true), Some("unknown"), Some(vec![STATS_ENDPOINT])); + assert!(!is_stats_computation_supported(&info)); + } + + #[test] + fn unsupported_when_stats_endpoint_absent() { + let info = make_agent_info(Some(true), Some("7.65.0"), Some(vec!["/v0.4/traces"])); + assert!(!is_stats_computation_supported(&info)); + + let info = make_agent_info(Some(true), Some("7.65.0"), None); + assert!(!is_stats_computation_supported(&info)); + } + } } diff --git a/libdd-data-pipeline/tests/test_fetch_info.rs b/libdd-data-pipeline/tests/test_fetch_info.rs index 81ec7acea8..a633e98930 100644 --- a/libdd-data-pipeline/tests/test_fetch_info.rs +++ b/libdd-data-pipeline/tests/test_fetch_info.rs @@ -6,31 +6,47 @@ mod tracing_integration_tests { use libdd_capabilities_impl::NativeCapabilities; use libdd_common::Endpoint; use libdd_data_pipeline::agent_info; + use libdd_data_pipeline::agent_info::schema::AgentVersion; use libdd_data_pipeline::agent_info::{fetch_info, AgentInfoFetcher}; use libdd_shared_runtime::Worker; use libdd_trace_utils::test_utils::datadog_test_agent::DatadogTestAgent; use std::time::Duration; + /// Override the test agent's reported `/info` version so it parses into a + /// real [`AgentVersion`] via the `TEST_AGENT_VERSION` env var. + const TEST_AGENT_VERSION: &str = "7.65.0"; + + fn expected_version() -> AgentVersion { + AgentVersion { + major: 7, + minor: 65, + patch: 0, + metadata: None, + } + } + #[cfg_attr(miri, ignore)] #[tokio::test] async fn test_fetch_info_from_test_agent() { - let test_agent = DatadogTestAgent::new(None, None, &[]).await; + let test_agent = + DatadogTestAgent::new(None, None, &[("TEST_AGENT_VERSION", TEST_AGENT_VERSION)]).await; let endpoint = Endpoint::from_url(test_agent.get_uri_for_endpoint("info", None).await); let info = fetch_info::(&endpoint) .await .expect("Failed to fetch agent info"); - assert!( + assert_eq!( info.info .version - .expect("Missing version field in agent response") - == "test" + .expect("Missing version field in agent response"), + expected_version(), ); } #[cfg_attr(miri, ignore)] #[tokio::test] async fn test_agent_info_fetcher_with_test_agent() { - let test_agent = DatadogTestAgent::new(None, None, &[]).await; + let test_agent = + DatadogTestAgent::new(None, None, &[("TEST_AGENT_VERSION", TEST_AGENT_VERSION)]).await; let endpoint = Endpoint::from_url(test_agent.get_uri_for_endpoint("info", None).await); let (mut fetcher, _response_observer) = AgentInfoFetcher::::new(endpoint, Duration::from_secs(1)); @@ -46,12 +62,12 @@ mod tracing_integration_tests { .await .expect("Agent request timed out"); - assert!( + assert_eq!( info.info .version .as_ref() - .expect("Missing version field in agent response") - == "test" + .expect("Missing version field in agent response"), + &expected_version(), ); } }