use http::HeaderMap;
use crate::operation::{
Checksum, CreateMultipartUploadRequest, ListMultipartUploadsRequest, ObjectKey, PageSize,
UploadId,
};
use crate::signing::{QueryParam, canonical_query};
use super::headers::{checksum_headers, create_headers};
use super::query::{list_parts_query, list_query, upload_part_query, upload_query};
fn encoded(query: &[(String, String)]) -> String {
canonical_query(
&query
.iter()
.map(|(name, value)| QueryParam::new(name, value))
.collect::<Vec<_>>(),
)
}
#[test]
fn upload_queries_encode_opaque_upload_ids_exactly_once() {
let upload_id = UploadId::new("id+/= value").unwrap();
assert_eq!(
encoded(&upload_part_query(7, &upload_id)),
"partNumber=7&uploadId=id%2B%2F%3D%20value"
);
assert_eq!(
encoded(&upload_query(&upload_id)),
"uploadId=id%2B%2F%3D%20value"
);
}
#[test]
fn list_query_contains_paired_resume_markers_and_bounds() {
let request = ListMultipartUploadsRequest {
prefix: Some("a b/".to_owned()),
delimiter: Some("/".to_owned()),
key_marker: Some("last+key".to_owned()),
upload_id_marker: Some(UploadId::new("upload/id=").unwrap()),
max_uploads: PageSize::new(17).unwrap(),
headers: HeaderMap::new(),
};
assert_eq!(
encoded(&list_query(&request).unwrap()),
"delimiter=%2F&key-marker=last%2Bkey&max-uploads=17&prefix=a%20b%2F&upload-id-marker=upload%2Fid%3D&uploads="
);
let invalid = ListMultipartUploadsRequest {
upload_id_marker: Some(UploadId::new("upload-id").unwrap()),
..ListMultipartUploadsRequest::default()
};
assert!(list_query(&invalid).is_err());
}
#[test]
fn list_parts_query_contains_upload_and_bounded_marker() {
let mut request = crate::ListPartsRequest::new(
ObjectKey::new("key").unwrap(),
UploadId::new("opaque /+").unwrap(),
);
request.part_number_marker = crate::PartNumber::new(7);
request.max_parts = PageSize::new(23).unwrap();
assert_eq!(
encoded(&list_parts_query(&request)),
"max-parts=23&part-number-marker=7&uploadId=opaque%20%2F%2B"
);
}
#[test]
fn custom_header_values_are_redacted() {
let mut request =
crate::ManagedMultipartUploadRequest::from_bytes(ObjectKey::new("key").unwrap(), "body");
request = request.with_metadata("metadata", "sentinel-metadata-value");
request.headers.upload_part.insert(
"x-amz-server-side-encryption-customer-key",
"sentinel-sse-c-key".parse().unwrap(),
);
let debug = format!("{request:?}");
assert!(debug.contains(r#"source: "bytes""#));
assert!(debug.contains(r#"user_metadata: ["metadata"]"#));
assert!(debug.contains(r#"upload_part: "<redacted>""#));
assert!(!debug.contains("body"));
assert!(!debug.contains("sentinel-metadata-value"));
assert!(!debug.contains("sentinel-sse-c-key"));
}
#[test]
fn request_header_state_is_validated_before_transport() {
let mut request = CreateMultipartUploadRequest::new(ObjectKey::new("key").unwrap());
request
.user_metadata
.insert(String::new(), "value".to_owned());
assert!(create_headers(&request).is_err());
let checksum = Checksum {
sha256: Some("not-base64".to_owned()),
..Checksum::default()
};
assert!(checksum_headers(&checksum, HeaderMap::new()).is_err());
let checksum = Checksum {
crc32: Some("AAAAAA==".to_owned()),
..Checksum::default()
};
assert_eq!(
checksum_headers(&checksum, HeaderMap::new())
.unwrap()
.get("x-amz-checksum-crc32")
.unwrap(),
"AAAAAA=="
);
}