Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions libdd-profiling/src/cxx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod ffi {
#[derive(Debug)]
#[repr(u8)]
enum MimeType {
None = 0,
ApplicationJson,
ApplicationOctetStream,
TextCsv,
Expand Down Expand Up @@ -335,6 +336,7 @@ impl TryFrom<ffi::MimeType> for exporter::MimeType {

fn try_from(mime: ffi::MimeType) -> Result<Self, Self::Error> {
match mime {
ffi::MimeType::None => Ok(exporter::MimeType::None),
ffi::MimeType::ApplicationJson => Ok(exporter::MimeType::ApplicationJson),
ffi::MimeType::ApplicationOctetStream => Ok(exporter::MimeType::ApplicationOctetStream),
ffi::MimeType::TextCsv => Ok(exporter::MimeType::TextCsv),
Expand Down Expand Up @@ -1115,6 +1117,18 @@ mod tests {
exporter::MimeType::ApplicationOctetStream
));

// AttachmentFile with MimeType::None
let file_none: exporter::File = (&ffi::AttachmentFile {
name: "test.dat",
data: &data,
mime: ffi::MimeType::None,
})
.try_into()
.expect("Failed to convert AttachmentFile with MimeType::None");
assert_eq!(file_none.name, "test.dat");
assert_eq!(file_none.bytes, data.as_slice());
assert!(matches!(file_none.mime, exporter::MimeType::None));

// Tag conversion with special characters
let tag: libdd_common::tag::Tag = (&ffi::Tag {
key: "test-key.with_special:chars",
Expand Down
16 changes: 11 additions & 5 deletions libdd-profiling/src/exporter/profile_exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub struct ProfileExporter {
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub enum MimeType {
None = 0,
ApplicationJson,
ApplicationOctetStream,
TextCsv,
Expand All @@ -61,6 +62,7 @@ pub enum MimeType {
impl MimeType {
pub fn as_str(&self) -> &'static str {
match self {
MimeType::None => "",
MimeType::ApplicationJson => mime::APPLICATION_JSON.as_ref(),
MimeType::ApplicationOctetStream => mime::APPLICATION_OCTET_STREAM.as_ref(),
MimeType::TextCsv => mime::TEXT_CSV.as_ref(),
Expand Down Expand Up @@ -426,16 +428,20 @@ impl ProfileExporter {
.context("failed to create compressor")?;
encoder.write_all(file.bytes)?;

form = form.part(
file.name.to_string(),
reqwest::multipart::Part::bytes(encoder.finish()?).file_name(file.name.to_string()),
);
let mut part =
reqwest::multipart::Part::bytes(encoder.finish()?).file_name(file.name.to_string());
if !matches!(file.mime, MimeType::None) {
part = part.mime_str(file.mime.as_str())?;
}
form = form.part(file.name.to_string(), part);
}

// Add profile
Ok(form.part(
"profile.pprof",
reqwest::multipart::Part::bytes(profile.buffer).file_name("profile.pprof"),
reqwest::multipart::Part::bytes(profile.buffer)
.file_name("profile.pprof")
.mime_str(mime::APPLICATION_OCTET_STREAM.as_ref())?,
))
}
}
77 changes: 63 additions & 14 deletions libdd-profiling/tests/exporter_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ async fn export_full_profile(
bytes: b"{\"test\": true}",
mime: MimeType::ApplicationJson,
},
File {
name: "custom.dat",
bytes: b"custom data",
mime: MimeType::None,
},
];

// Build metadata
Expand Down Expand Up @@ -348,8 +353,8 @@ fn validate_full_export(req: &ReceivedRequest, expected_path: &str) -> anyhow::R
let attachments = event_json["attachments"]
.as_array()
.ok_or_else(|| anyhow::anyhow!("Missing attachments"))?;
assert_eq!(attachments.len(), 3);
for attachment in &["profile.pprof", "jit.pprof", "metadata.json"] {
assert_eq!(attachments.len(), 4);
for attachment in &["profile.pprof", "jit.pprof", "metadata.json", "custom.dat"] {
assert!(
attachments.contains(&serde_json::json!(attachment)),
"Missing attachment: {}",
Expand All @@ -371,18 +376,62 @@ fn validate_full_export(req: &ReceivedRequest, expected_path: &str) -> anyhow::R
assert_eq!(event_json["info"]["platform"]["hostname"], "test-host");
assert_eq!(event_json["info"]["runtime"]["engine"], "rust");

// Verify parts exist (files are compressed, just check non-empty)
for part_name in &["profile.pprof", "jit.pprof", "metadata.json"] {
let part = parts
.iter()
.find(|p| p.name == *part_name)
.ok_or_else(|| anyhow::anyhow!("Missing part: {}", part_name))?;
assert!(
!part.content.is_empty(),
"{} should not be empty",
part_name
);
}
// Verify parts exist (files are compressed, just check non-empty) and mime types
let profile_part = parts
.iter()
.find(|p| p.name == "profile.pprof")
.ok_or_else(|| anyhow::anyhow!("Missing part: profile.pprof"))?;
assert!(
!profile_part.content.is_empty(),
"profile.pprof should not be empty"
);
assert_eq!(
profile_part.content_type.as_deref(),
Some("application/octet-stream"),
"profile.pprof should have application/octet-stream mime type"
);

let jit_part = parts
.iter()
.find(|p| p.name == "jit.pprof")
.ok_or_else(|| anyhow::anyhow!("Missing part: jit.pprof"))?;
assert!(
!jit_part.content.is_empty(),
"jit.pprof should not be empty"
);
assert_eq!(
jit_part.content_type.as_deref(),
Some("application/octet-stream"),
"jit.pprof should have application/octet-stream mime type"
);

let metadata_part = parts
.iter()
.find(|p| p.name == "metadata.json")
.ok_or_else(|| anyhow::anyhow!("Missing part: metadata.json"))?;
assert!(
!metadata_part.content.is_empty(),
"metadata.json should not be empty"
);
assert_eq!(
metadata_part.content_type.as_deref(),
Some("application/json"),
"metadata.json should have application/json mime type"
);

let custom_part = parts
.iter()
.find(|p| p.name == "custom.dat")
.ok_or_else(|| anyhow::anyhow!("Missing part: custom.dat"))?;
assert!(
!custom_part.content.is_empty(),
"custom.dat should not be empty"
);
// When MimeType::None is used, no content_type should be set
assert!(
custom_part.content_type.is_none(),
"custom.dat should not have a mime type (MimeType::None)"
);

Ok(())
}
Expand Down
5 changes: 5 additions & 0 deletions libdd-profiling/tests/file_exporter_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ mod tests {
!profile_part.content.is_empty(),
"profile should have content"
);
assert_eq!(
profile_part.content_type.as_deref(),
Some("application/octet-stream"),
"profile.pprof should have application/octet-stream mime type"
);
}

#[test]
Expand Down
Loading