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>"9a9d1bbe80188883302bff764b4cb321"</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>"ddcaa99616d7cd06d0a5abfef6ccebbb"</ETag>
206 <Size>5242880</Size>
207 </Part>
208 <Part>
209 <PartNumber>2</PartNumber>
210 <LastModified>2015-09-08T21:02:09.000Z</LastModified>
211 <ETag>"c865f7d241e2c9e3d3b5fee6955c616e"</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