use libdd_profiling::exporter::{ProfileExporter, Request};
use libdd_profiling::internal::EncodedProfile;
fn multipart(
exporter: &mut ProfileExporter,
internal_metadata: Option<serde_json::Value>,
info: Option<serde_json::Value>,
) -> Request {
let profile = EncodedProfile::test_instance().expect("To get a profile");
let files_to_compress_and_export = &[];
let files_to_export_unmodified = &[];
let timeout: u64 = 10_000;
exporter.set_timeout(timeout);
let request = exporter
.build(
profile,
files_to_compress_and_export,
files_to_export_unmodified,
None,
internal_metadata,
info,
)
.expect("request to be built");
let actual_timeout = request.timeout().expect("timeout to exist");
assert_eq!(actual_timeout, std::time::Duration::from_millis(timeout));
request
}
#[cfg(test)]
mod tests {
use crate::multipart;
use http_body_util::BodyExt;
use libdd_common::tag;
use libdd_profiling::exporter::*;
use serde_json::json;
fn default_tags() -> Vec<Tag> {
vec![tag!("service", "php"), tag!("host", "bits")]
}
fn parsed_event_json(request: Request) -> serde_json::Value {
let body = request.body();
let body_bytes: String = String::from_utf8_lossy(
&futures::executor::block_on(body.collect())
.unwrap()
.to_bytes(),
)
.to_string();
let event_json = body_bytes
.lines()
.skip_while(|line| !line.contains(r#"filename="event.json""#))
.nth(2)
.unwrap();
serde_json::from_str(event_json).unwrap()
}
#[test]
#[cfg_attr(miri, ignore)]
fn multipart_agent() {
let profiling_library_name = "dd-trace-foo";
let profiling_library_version = "1.2.3";
let base_url = "http://localhost:8126".parse().expect("url to parse");
let endpoint = config::agent(base_url).expect("endpoint to construct");
let mut exporter = ProfileExporter::new(
profiling_library_name,
profiling_library_version,
"php",
Some(default_tags()),
endpoint,
)
.expect("exporter to construct");
let request = multipart(&mut exporter, None, None);
assert_eq!(
request.uri().to_string(),
"http://localhost:8126/profiling/v1/input"
);
let actual_headers = request.headers();
assert!(!actual_headers.contains_key("DD-API-KEY"));
assert_eq!(
actual_headers.get("DD-EVP-ORIGIN").unwrap(),
profiling_library_name
);
assert_eq!(
actual_headers.get("DD-EVP-ORIGIN-VERSION").unwrap(),
profiling_library_version
);
let parsed_event_json = parsed_event_json(request);
assert_eq!(parsed_event_json["attachments"], json!(["profile.pprof"]));
assert_eq!(parsed_event_json["endpoint_counts"], json!(null));
assert_eq!(parsed_event_json["family"], json!("php"));
assert_eq!(
parsed_event_json["internal"],
json!({"libdatadog_version": env!("CARGO_PKG_VERSION")})
);
let tags_profiler = parsed_event_json["tags_profiler"]
.as_str()
.unwrap()
.split(',')
.collect::<Vec<_>>();
assert!(tags_profiler.contains(&"service:php"));
assert!(tags_profiler.contains(&"host:bits"));
let runtime_platform = tags_profiler
.iter()
.find(|tag| tag.starts_with("runtime_platform:"))
.expect("runtime_platform tag should exist");
assert!(
runtime_platform.starts_with(&format!("runtime_platform:{}", std::env::consts::ARCH)),
"expected platform tag to start with runtime_platform:{} but got '{}'",
std::env::consts::ARCH,
runtime_platform
);
assert_eq!(parsed_event_json["version"], json!("4"));
}
#[test]
#[cfg_attr(miri, ignore)]
fn including_internal_metadata() {
let profiling_library_name = "dd-trace-foo";
let profiling_library_version = "1.2.3";
let base_url = "http://localhost:8126".parse().expect("url to parse");
let endpoint = config::agent(base_url).expect("endpoint to construct");
let mut exporter = ProfileExporter::new(
profiling_library_name,
profiling_library_version,
"php",
Some(default_tags()),
endpoint,
)
.expect("exporter to construct");
let internal_metadata = json!({
"no_signals_workaround_enabled": "true",
"execution_trace_enabled": "false",
"extra object": {"key": [1, 2, true]},
"libdatadog_version": env!("CARGO_PKG_VERSION"),
});
let request = multipart(&mut exporter, Some(internal_metadata.clone()), None);
let parsed_event_json = parsed_event_json(request);
assert_eq!(parsed_event_json["internal"], internal_metadata);
}
#[test]
#[cfg_attr(miri, ignore)]
fn including_info() {
let profiling_library_name = "dd-trace-foo";
let profiling_library_version = "1.2.3";
let base_url = "http://localhost:8126".parse().expect("url to parse");
let endpoint = config::agent(base_url).expect("endpoint to construct");
let mut exporter = ProfileExporter::new(
profiling_library_name,
profiling_library_version,
"php",
Some(default_tags()),
endpoint,
)
.expect("exporter to construct");
let info = json!({
"application": {
"start_time": "2024-01-24T11:17:22+0000",
"env": "test"
},
"runtime": {
"engine": "ruby",
"version": "3.2.0",
"platform": "arm64-darwin22"
},
"profiler": {
"version": "1.32.0",
"libdatadog": "1.2.3-darwin",
"settings": {}
}
});
let request = multipart(&mut exporter, None, Some(info.clone()));
let parsed_event_json = parsed_event_json(request);
assert_eq!(parsed_event_json["info"], info);
}
#[test]
#[cfg_attr(miri, ignore)]
fn multipart_agentless() {
let profiling_library_name = "dd-trace-foo";
let profiling_library_version = "1.2.3";
let api_key = "1234567890123456789012";
let endpoint = config::agentless("datadoghq.com", api_key).expect("endpoint to construct");
let mut exporter = ProfileExporter::new(
profiling_library_name,
profiling_library_version,
"php",
Some(default_tags()),
endpoint,
)
.expect("exporter to construct");
let request = multipart(&mut exporter, None, None);
assert_eq!(
request.uri().to_string(),
"https://intake.profile.datadoghq.com/api/v2/profile"
);
let actual_headers = request.headers();
assert_eq!(actual_headers.get("DD-API-KEY").unwrap(), api_key);
assert_eq!(
actual_headers.get("DD-EVP-ORIGIN").unwrap(),
profiling_library_name
);
assert_eq!(
actual_headers.get("DD-EVP-ORIGIN-VERSION").unwrap(),
profiling_library_version
);
}
}