1 extern crate rusoto_mock;
2 
3 use crate::generated::*;
4 
5 use self::rusoto_mock::*;
6 use bytes::BytesMut;
7 use futures::TryStreamExt;
8 use rusoto_core::signature::SignedRequest;
9 use rusoto_core::{Region, RusotoError};
10 
11 #[tokio::test]
test_multipart_upload_copy_response()12 async fn test_multipart_upload_copy_response() {
13     let mock = MockRequestDispatcher::with_status(200).with_body(
14         r#"<?xml version="1.0" encoding="UTF-8"?>
15             <CopyPartResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
16                 <LastModified>2018-11-10T02:47:18.000Z</LastModified>
17                 <ETag>&quot;9a9d1bbe80188883302bff764b4cb321&quot;</ETag>
18             </CopyPartResult>"#,
19     );
20     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
21     let upload_part_copy_req = UploadPartCopyRequest {
22         key: "multipartfilename".to_string(),
23         bucket: "fakebucket".to_owned(),
24         part_number: 2,
25         upload_id: "fake".to_string(),
26         copy_source: "fakebuket/fake".to_string(),
27         ..Default::default()
28     };
29     let result = client.upload_part_copy(upload_part_copy_req).await.unwrap();
30     assert!(
31         result.copy_part_result.is_some(),
32         "Should have result in etag field"
33     );
34     assert_eq!(
35         result.copy_part_result.unwrap().e_tag.unwrap(),
36         "\"9a9d1bbe80188883302bff764b4cb321\""
37     );
38 }
39 
40 #[tokio::test]
test_list_object_versions_with_multiple_versions()41 async fn test_list_object_versions_with_multiple_versions() {
42     let mock = MockRequestDispatcher::with_status(200).with_body(
43         r#"<?xml version="1.0" encoding="UTF-8"?>
44         <ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
45           <DeleteMarker>
46           </DeleteMarker>
47           <Version>
48           </Version>
49           <DeleteMarker>
50           </DeleteMarker>
51           <Version>
52           </Version>
53         </ListVersionsResult>
54         "#,
55     );
56     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
57     let result = client
58         .list_object_versions(ListObjectVersionsRequest {
59             bucket: "test_bucket".to_string(),
60             ..Default::default()
61         })
62         .await
63         .unwrap();
64     assert_eq!(result.versions.unwrap().len(), 2);
65     assert_eq!(result.delete_markers.unwrap().len(), 2);
66 }
67 
68 #[tokio::test]
initiate_multipart_upload_happy_path()69 async fn initiate_multipart_upload_happy_path() {
70     let body = MockResponseReader::read_response(
71         "test_resources/custom",
72         "s3_initiate_multipart_upload.xml",
73     );
74     let mock = MockRequestDispatcher::with_status(200).with_body(&body);
75 
76     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
77     let result = client
78         .create_multipart_upload(CreateMultipartUploadRequest {
79             bucket: "example-bucket".to_owned(),
80             key: "example-object".to_owned(),
81             ..Default::default()
82         })
83         .await;
84 
85     match result {
86         Err(_) => panic!("Couldn't parse initiate_multipart_upload"),
87         Ok(result) => {
88             assert_eq!(sstr("example-bucket"), result.bucket);
89             assert_eq!(sstr("example-object"), result.key);
90             assert_eq!(
91                 sstr("VXBsb2FkIElEIGZvciA2aWWpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA"),
92                 result.upload_id
93             );
94         }
95     }
96 }
97 
98 #[tokio::test]
complete_multipart_upload_happy_path()99 async fn complete_multipart_upload_happy_path() {
100     let body = MockResponseReader::read_response(
101         "test_resources/custom",
102         "s3_complete_multipart_upload.xml",
103     );
104     let mock = MockRequestDispatcher::with_status(200).with_body(&body);
105 
106     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
107     let result = client
108         .complete_multipart_upload(CompleteMultipartUploadRequest {
109             bucket: "example-bucket".to_owned(),
110             key: "example-object".to_owned(),
111             upload_id: "VXBsb2FkIElEIGZvciA2aWWpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA".to_owned(),
112             ..Default::default()
113         })
114         .await;
115 
116     match result {
117         Err(_) => panic!("Couldn't parse s3_complete_multipart_upload"),
118         Ok(result) => {
119             assert_eq!(result.bucket, sstr("testbucket2"));
120             assert_eq!(result.key, sstr("foo.zip"));
121             assert_eq!(result.e_tag, sstr("\"525a81fcbc4181997bd96e4096fa7304-1\""));
122         }
123     }
124 }
125 
126 #[tokio::test]
list_multipart_upload_happy_path()127 async fn list_multipart_upload_happy_path() {
128     let body =
129         MockResponseReader::read_response("test_resources/custom", "s3_list_multipart_uploads.xml");
130     let mock = MockRequestDispatcher::with_status(200).with_body(&body);
131 
132     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
133     let result = client
134         .list_multipart_uploads(ListMultipartUploadsRequest {
135             bucket: "example-bucket".to_owned(),
136             ..Default::default()
137         })
138         .await;
139 
140     match result {
141         Err(_) => panic!("Couldn't parse s3_list_multipart_uploads.xml"),
142         Ok(result) => {
143             assert_eq!(result.bucket, sstr("rusoto1440826511"));
144             assert!(result.uploads.is_some());
145 
146             let an_upload = &result.uploads.unwrap()[0];
147             assert_eq!(an_upload.upload_id,
148                        sstr("eUeGzA6xR2jAH7KUhTSwrrNVfu8XPIYdoWpa7meOiceoGQLQhtKfPg_APCnuVRsyWd7bx8SS5jNssgdtTU5tTziGOz.j1URgseoqpdHqnyZRikJHTLd6iXF.GjKBEhky"));
149             assert_eq!(an_upload.key, sstr("join.me.zip"));
150 
151             let test_initiator = Initiator {
152                 id: sstr("arn:aws:iam::347452556412:user/matthew"),
153                 display_name: sstr("matthew"),
154             };
155 
156             assert_eq!(an_upload.initiator.as_ref().unwrap().id, test_initiator.id);
157             assert_eq!(
158                 an_upload.initiator.as_ref().unwrap().display_name,
159                 test_initiator.display_name
160             );
161 
162             assert_eq!(an_upload.initiated, sstr("2015-09-01T19:22:56.000Z"));
163 
164             let test_owner = Owner {
165                 id: sstr("b84c6b0c308085829b6562b586f6664fc00faab6cfd441e90ad418ea916eed83"),
166                 display_name: sstr("matthew"),
167             };
168 
169             assert_eq!(an_upload.owner.as_ref().unwrap().id, test_owner.id);
170             assert_eq!(
171                 an_upload.owner.as_ref().unwrap().display_name,
172                 test_owner.display_name
173             );
174 
175             assert_eq!(an_upload.storage_class, sstr("STANDARD"));
176         }
177     }
178 }
179 
180 #[tokio::test]
list_multipart_upload_parts_happy_path()181 async fn list_multipart_upload_parts_happy_path() {
182     let mock = MockRequestDispatcher::with_status(200)
183         .with_body(r#"
184         <?xml version="1.0" encoding="UTF-8"?>
185         <ListPartsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
186             <Bucket>rusoto1440826511</Bucket>
187             <Key>testfile.zip</Key>
188             <UploadId>PeePB_uORK5f2AURP_SWcQ4NO1P1oqnGNNNFK3nhFfzMeksdvG7x7nFfH1qk7a3HSossNYB7t8QhcN1Fg6ax7AXbwvAKIZ9DilB4tUcpM7qyUEgkszN4iDmMvSaImGFK</UploadId>
189             <Initiator>
190                 <ID>arn:aws:iam::347452556412:user/matthew</ID>
191                 <DisplayName>matthew</DisplayName>
192             </Initiator>
193             <Owner>
194                 <ID>b84c6b0c308085829b6562b586f6664fc00faab6cfd441e90ad418ea916eed83</ID>
195                 <DisplayName>matthew</DisplayName>
196             </Owner>
197             <StorageClass>STANDARD</StorageClass>
198             <PartNumberMarker>0</PartNumberMarker>
199             <NextPartNumberMarker>2</NextPartNumberMarker>
200             <MaxParts>1000</MaxParts>
201             <IsTruncated>false</IsTruncated>
202             <Part>
203                 <PartNumber>1</PartNumber>
204                 <LastModified>2015-09-08T21:02:04.000Z</LastModified>
205                 <ETag>&quot;ddcaa99616d7cd06d0a5abfef6ccebbb&quot;</ETag>
206                 <Size>5242880</Size>
207             </Part>
208             <Part>
209                 <PartNumber>2</PartNumber>
210                 <LastModified>2015-09-08T21:02:09.000Z</LastModified>
211                 <ETag>&quot;c865f7d241e2c9e3d3b5fee6955c616e&quot;</ETag>
212                 <Size>5242880</Size>
213             </Part>
214         </ListPartsResult>"#)
215         .with_request_checker(|request: &SignedRequest| {
216             assert_eq!(request.method, "GET");
217             assert_eq!(request.path, "/rusoto1440826511/testfile.zip");
218             assert!(request.payload.is_none());
219         });
220 
221     let mut req = ListPartsRequest::default();
222     req.bucket = "rusoto1440826511".to_owned();
223     req.key = "testfile.zip".to_owned();
224 
225     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
226     let result = client.list_parts(req).await.unwrap();
227     assert_eq!(result.bucket, sstr("rusoto1440826511"));
228     assert_eq!(result.upload_id,
229                sstr("PeePB_uORK5f2AURP_SWcQ4NO1P1oqnGNNNFK3nhFfzMeksdvG7x7nFfH1qk7a3HSossNYB7t8QhcN1Fg6ax7AXbwvAKIZ9DilB4tUcpM7qyUEgkszN4iDmMvSaImGFK"));
230     assert_eq!(result.key, sstr("testfile.zip"));
231 
232     let test_initiator = Initiator {
233         id: sstr("arn:aws:iam::347452556412:user/matthew"),
234         display_name: sstr("matthew"),
235     };
236 
237     assert_eq!(result.initiator.as_ref().unwrap().id, test_initiator.id);
238     assert_eq!(
239         result.initiator.as_ref().unwrap().display_name,
240         test_initiator.display_name
241     );
242 
243     let test_owner = Owner {
244         id: sstr("b84c6b0c308085829b6562b586f6664fc00faab6cfd441e90ad418ea916eed83"),
245         display_name: sstr("matthew"),
246     };
247 
248     assert_eq!(result.owner.as_ref().unwrap().id, test_owner.id);
249     assert_eq!(
250         result.owner.as_ref().unwrap().display_name,
251         test_owner.display_name
252     );
253 
254     assert_eq!(result.storage_class, sstr("STANDARD"));
255 
256     assert!(result.parts.is_some());
257 
258     let parts = result.parts.unwrap();
259     assert_eq!(parts.len(), 2);
260 
261     assert_eq!(parts[0].part_number, Some(1));
262     assert_eq!(parts[0].e_tag, sstr("\"ddcaa99616d7cd06d0a5abfef6ccebbb\""));
263     assert_eq!(parts[0].size, Some(5_242_880));
264     assert_eq!(parts[0].last_modified, sstr("2015-09-08T21:02:04.000Z"));
265 }
266 
267 #[tokio::test]
list_multipart_uploads_no_uploads()268 async fn list_multipart_uploads_no_uploads() {
269     let mock = MockRequestDispatcher::with_status(200).with_body(
270         r#"
271         <?xml version="1.0" encoding="UTF-8"?>
272         <ListMultipartUploadsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
273             <Bucket>rusoto1440826568</Bucket>
274             <KeyMarker></KeyMarker>
275             <UploadIdMarker></UploadIdMarker>
276             <NextKeyMarker></NextKeyMarker>
277             <NextUploadIdMarker></NextUploadIdMarker>
278             <MaxUploads>1000</MaxUploads>
279             <IsTruncated>false</IsTruncated>
280         </ListMultipartUploadsResult>
281     "#,
282     );
283 
284     let mut req = ListMultipartUploadsRequest::default();
285     req.bucket = "test-bucket".to_owned();
286 
287     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
288     let result = client.list_multipart_uploads(req).await.unwrap();
289 
290     assert_eq!(result.bucket, sstr("rusoto1440826568"));
291     assert!(result.uploads.is_none());
292 }
293 
294 #[cfg(nightly)]
295 #[bench]
bench_parse_list_buckets_response(b: &mut Bencher)296 fn bench_parse_list_buckets_response(b: &mut Bencher) {
297     use test::Bencher;
298     let mut rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
299     let mock = MockRequestDispatcher::with_status(200)
300         .with_body(
301             r#"
302         <?xml version="1.0" encoding="UTF-8"?>
303         <ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
304             <Owner>
305             <ID>bcaf1ffd86f461ca5fb16fd081034f</ID>
306             <DisplayName>webfile</DisplayName>
307             </Owner>
308             <Buckets>
309             <Bucket>
310                     <Name>quotes</Name>
311                     <CreationDate>2006-02-03T16:45:09.000Z</CreationDate>
312             </Bucket>
313             <Bucket>
314                     <Name>samples</Name>
315                     <CreationDate>2006-02-03T16:41:58.000Z</CreationDate>
316             </Bucket>
317             </Buckets>
318         </ListAllMyBucketsResult>
319         "#,
320         )
321         .with_request_checker(|request: &SignedRequest| {
322             assert_eq!(request.method, "GET");
323             assert_eq!(request.path, "/");
324             assert!(request.payload.is_none());
325         });
326 
327     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
328 
329     b.iter(|| rt.block_on(client.list_buckets()).unwrap());
330 }
331 
332 #[tokio::test]
333 // sample response from the S3 documentation
334 // tests the model generation and deserialization end-to-end
should_parse_sample_list_buckets_response()335 async fn should_parse_sample_list_buckets_response() {
336     let mock = MockRequestDispatcher::with_status(200)
337         .with_body(
338             r#"
339         <?xml version="1.0" encoding="UTF-8"?>
340         <ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
341             <Owner>
342             <ID>bcaf1ffd86f461ca5fb16fd081034f</ID>
343             <DisplayName>webfile</DisplayName>
344             </Owner>
345             <Buckets>
346             <Bucket>
347                     <Name>quotes</Name>
348                     <CreationDate>2006-02-03T16:45:09.000Z</CreationDate>
349             </Bucket>
350             <Bucket>
351                     <Name>samples</Name>
352                     <CreationDate>2006-02-03T16:41:58.000Z</CreationDate>
353             </Bucket>
354             </Buckets>
355         </ListAllMyBucketsResult>
356         "#,
357         )
358         .with_request_checker(|request: &SignedRequest| {
359             assert_eq!(request.method, "GET");
360             assert_eq!(request.path, "/");
361             assert!(request.payload.is_none());
362         });
363 
364     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
365     let result = client.list_buckets().await.unwrap();
366 
367     let owner = result.owner.unwrap();
368     assert_eq!(owner.display_name, Some("webfile".to_string()));
369     assert_eq!(owner.id, Some("bcaf1ffd86f461ca5fb16fd081034f".to_string()));
370 
371     let buckets = result.buckets.unwrap();
372     assert_eq!(buckets.len(), 2);
373 
374     let bucket1 = buckets.get(0).unwrap();
375     assert_eq!(bucket1.name, Some("quotes".to_string()));
376     assert_eq!(
377         bucket1.creation_date,
378         Some("2006-02-03T16:45:09.000Z".to_string())
379     );
380 }
381 
382 #[tokio::test]
hould_parse_headers()383 async fn hould_parse_headers() {
384     let mock = MockRequestDispatcher::with_status(200)
385         .with_body("")
386         .with_header("x-amz-expiration", "foo")
387         .with_header("x-amz-restore", "bar");
388 
389     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
390     let request = HeadObjectRequest::default();
391     let result = client.head_object(request).await.unwrap();
392 
393     assert_eq!(result.expiration, Some("foo".to_string()));
394     assert_eq!(result.restore, Some("bar".to_string()));
395 }
396 
397 #[tokio::test]
should_serialize_complicated_request()398 async fn should_serialize_complicated_request() {
399     let request = GetObjectRequest {
400         bucket: "bucket".to_string(),
401         if_match: sstr("if_match"),
402         if_modified_since: sstr("if_modified_since"),
403         if_none_match: sstr("if_none_match"),
404         if_unmodified_since: sstr("if_unmodified_since"),
405         key: "key".to_string(),
406         part_number: Some(1),
407         range: sstr("range"),
408         request_payer: sstr("request_payer"),
409         response_cache_control: sstr("response_cache_control"),
410         response_content_disposition: sstr("response_content_disposition"),
411         response_content_encoding: sstr("response_content_encoding"),
412         response_content_language: sstr("response_content_language"),
413         response_content_type: sstr("response_content_type"),
414         response_expires: sstr("response_expires"),
415         sse_customer_algorithm: sstr("sse_customer_algorithm"),
416         sse_customer_key: sstr("sse_customer_key"),
417         sse_customer_key_md5: sstr("sse_customer_key_md5"),
418         version_id: sstr("version_id"),
419     };
420 
421     let mock = MockRequestDispatcher::with_status(200)
422         .with_body("")
423         .with_request_checker(|request: &SignedRequest| {
424             assert_eq!(request.method, "GET");
425             assert_eq!(request.path, "/bucket/key");
426             assert_eq!(
427                 *request.params.get("response-content-type").unwrap(),
428                 sstr("response_content_type")
429             );
430             assert!(request
431                 .headers
432                 .get("range")
433                 .unwrap()
434                 .contains(&Vec::from("range")));
435             assert!(request.payload.is_none());
436         });
437 
438     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
439     let _ = client.get_object(request).await.unwrap();
440 }
441 
442 #[tokio::test]
should_parse_location_constraint()443 async fn should_parse_location_constraint() {
444     let body = MockResponseReader::read_response(
445         "test_resources/generated/valid",
446         "s3-get-bucket-location.xml",
447     );
448     let mock = MockRequestDispatcher::with_status(200).with_body(&body);
449 
450     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
451     let result = client
452         .get_bucket_location(GetBucketLocationRequest {
453             bucket: "example-bucket".to_owned(),
454         })
455         .await;
456 
457     match result {
458         Err(_) => panic!("Couldn't parse get_bucket_location"),
459         Ok(result) => {
460             assert_eq!(sstr("EU"), result.location_constraint);
461         }
462     }
463 }
464 
465 #[tokio::test]
can_construct_streaming_body()466 async fn can_construct_streaming_body() {
467     let test_body = ::futures::stream::once(futures::future::ready(Ok("Simple Body Test"
468         .to_owned()
469         .into())));
470     let streaming_body = StreamingBody::new(test_body);
471     let bytes: BytesMut = streaming_body
472         .map_ok(|b| BytesMut::from(&b[..]))
473         .try_concat()
474         .await
475         .unwrap();
476     let read_string = std::str::from_utf8(bytes.as_ref()).unwrap();
477     assert_eq!("Simple Body Test", read_string);
478 }
479 
480 #[test]
structs_should_impl_clone()481 fn structs_should_impl_clone() {
482     fn assert_clone<T: Clone>() {}
483 
484     // GetObjectRequest is common but simple. It must impl Clone.
485     // (But note GetObjectOutput cannot impl Clone because it uses streaming.)
486     assert_clone::<GetObjectRequest>();
487     // RestoreObjectRequest has several layers of structs, all of which must impl Clone.
488     assert_clone::<RestoreObjectRequest>();
489 }
490 
491 /// returns Some(String)
sstr(value: &'static str) -> Option<String>492 fn sstr(value: &'static str) -> Option<String> {
493     Some(value.to_string())
494 }
495 
496 #[tokio::test]
test_parse_no_such_bucket_error()497 async fn test_parse_no_such_bucket_error() {
498     let mock = MockRequestDispatcher::with_status(404).with_body(
499         r#"<?xml version="1.0" encoding="UTF-8"?>
500         <Error>
501             <Code>NoSuchBucket</Code>
502             <Message>The specified bucket does not exist</Message>
503             <RequestId>4442587FB7D0A2F9</RequestId>
504         </Error>"#,
505     );
506 
507     let request = ListObjectsV2Request {
508         bucket: "no-such-bucket".to_owned(),
509         ..Default::default()
510     };
511 
512     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
513     let result = client.list_objects_v2(request).await;
514     assert!(result.is_err());
515     let err = result.err().unwrap();
516     assert_eq!(
517         RusotoError::Service(ListObjectsV2Error::NoSuchBucket(
518             "The specified bucket does not exist".to_owned()
519         )),
520         err
521     );
522 }
523 
524 #[tokio::test]
test_multiple_mock()525 async fn test_multiple_mock() {
526     let mock = MultipleMockRequestDispatcher::new(vec![
527         MockRequestDispatcher::with_status(200)
528             .with_body("")
529             .with_header("x-amz-expiration", "foo1")
530             .with_header("x-amz-restore", "bar1"),
531         MockRequestDispatcher::with_status(200)
532             .with_body("")
533             .with_header("x-amz-expiration", "foo2")
534             .with_header("x-amz-restore", "bar2"),
535     ]);
536 
537     let client = S3Client::new_with(mock, MockCredentialsProvider, Region::UsEast1);
538 
539     let mut request = HeadObjectRequest::default();
540     let mut result = client.head_object(request).await.unwrap();
541     assert_eq!(result.expiration, Some("foo1".to_string()));
542     assert_eq!(result.restore, Some("bar1".to_string()));
543 
544     request = HeadObjectRequest::default();
545     result = client.head_object(request).await.unwrap();
546     assert_eq!(result.expiration, Some("foo2".to_string()));
547     assert_eq!(result.restore, Some("bar2".to_string()));
548 }
549