1 + | /*
|
2 + | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3 + | * SPDX-License-Identifier: Apache-2.0
|
4 + | */
|
5 + |
|
6 + | //! Integration tests for `aws-smithy-mocks`. These tests are not necessarily specific to S3 but
|
7 + | //! we need to test the macros against an actual SDK.
|
8 + |
|
9 + | use aws_sdk_s3::config::retry::RetryConfig;
|
10 + | use aws_sdk_s3::operation::get_object::{GetObjectError, GetObjectOutput};
|
11 + | use aws_sdk_s3::operation::list_buckets::ListBucketsError;
|
12 + | use aws_smithy_mocks::{mock, mock_client, RuleMode};
|
13 + | use aws_smithy_runtime_api::client::orchestrator::HttpResponse;
|
14 + | use aws_smithy_runtime_api::http::StatusCode;
|
15 + | use aws_smithy_types::body::SdkBody;
|
16 + | use aws_smithy_types::byte_stream::ByteStream;
|
17 + | use aws_smithy_types::error::metadata::ProvideErrorMetadata;
|
18 + | use aws_smithy_types::error::ErrorMetadata;
|
19 + |
|
20 + | const S3_NO_SUCH_KEY: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
|
21 + | <Error>
|
22 + | <Code>NoSuchKey</Code>
|
23 + | <Message>The resource you requested does not exist</Message>
|
24 + | <Resource>/mybucket/myfoto.jpg</Resource>
|
25 + | <RequestId>4442587FB7D0A2F9</RequestId>
|
26 + | </Error>"#;
|
27 + |
|
28 + | #[tokio::test]
|
29 + | async fn test_mock_client() {
|
30 + | let s3_404 = mock!(aws_sdk_s3::Client::get_object)
|
31 + | .match_requests(|inp| {
|
32 + | inp.bucket() == Some("test-bucket") && inp.key() != Some("correct-key")
|
33 + | })
|
34 + | .then_http_response(|| {
|
35 + | HttpResponse::new(
|
36 + | StatusCode::try_from(400).unwrap(),
|
37 + | SdkBody::from(S3_NO_SUCH_KEY),
|
38 + | )
|
39 + | });
|
40 + |
|
41 + | let s3_real_object = mock!(aws_sdk_s3::Client::get_object)
|
42 + | .match_requests(|inp| {
|
43 + | inp.bucket() == Some("test-bucket") && inp.key() == Some("correct-key")
|
44 + | })
|
45 + | .then_output(|| {
|
46 + | GetObjectOutput::builder()
|
47 + | .body(ByteStream::from_static(b"test-test-test"))
|
48 + | .build()
|
49 + | });
|
50 + |
|
51 + | let modeled_error = mock!(aws_sdk_s3::Client::list_buckets).then_error(|| {
|
52 + | ListBucketsError::generic(ErrorMetadata::builder().code("InvalidAccessKey").build())
|
53 + | });
|
54 + |
|
55 + | let s3 = mock_client!(aws_sdk_s3, &[&s3_404, &s3_real_object, &modeled_error]);
|
56 + |
|
57 + | let error = s3
|
58 + | .get_object()
|
59 + | .bucket("test-bucket")
|
60 + | .key("foo")
|
61 + | .send()
|
62 + | .await
|
63 + | .expect_err("404");
|
64 + |
|
65 + | assert!(matches!(
|
66 + | error.into_service_error(),
|
67 + | GetObjectError::NoSuchKey(_)
|
68 + | ));
|
69 + |
|
70 + | assert_eq!(s3_404.num_calls(), 1);
|
71 + |
|
72 + | let data = s3
|
73 + | .get_object()
|
74 + | .bucket("test-bucket")
|
75 + | .key("correct-key")
|
76 + | .send()
|
77 + | .await
|
78 + | .expect("success response")
|
79 + | .body
|
80 + | .collect()
|
81 + | .await
|
82 + | .expect("successful read")
|
83 + | .to_vec();
|
84 + |
|
85 + | assert_eq!(data, b"test-test-test");
|
86 + | assert_eq!(s3_real_object.num_calls(), 1);
|
87 + |
|
88 + | let err = s3.list_buckets().send().await.expect_err("bad access key");
|
89 + | assert_eq!(err.code(), Some("InvalidAccessKey"));
|
90 + | }
|
91 + |
|
92 + | #[tokio::test]
|
93 + | async fn test_mock_client_sequence() {
|
94 + | let rule = mock!(aws_sdk_s3::Client::get_object)
|
95 + | .sequence()
|
96 + | .http_status(400, Some(S3_NO_SUCH_KEY.to_string()))
|
97 + | .output(|| {
|
98 + | GetObjectOutput::builder()
|
99 + | .body(ByteStream::from_static(b"test-test-test"))
|
100 + | .build()
|
101 + | })
|
102 + | .build();
|
103 + |
|
104 + | // test client builder override
|
105 + | let s3 = mock_client!(
|
106 + | aws_sdk_s3,
|
107 + | RuleMode::Sequential,
|
108 + | [&rule],
|
109 + | |client_builder| { client_builder.endpoint_url("http://localhost:9000") }
|
110 + | );
|
111 + |
|
112 + | let error = s3
|
113 + | .get_object()
|
114 + | .bucket("test-bucket")
|
115 + | .key("foo")
|
116 + | .send()
|
117 + | .await
|
118 + | .expect_err("404");
|
119 + |
|
120 + | assert!(matches!(
|
121 + | error.into_service_error(),
|
122 + | GetObjectError::NoSuchKey(_)
|
123 + | ));
|
124 + | assert_eq!(1, rule.num_calls());
|
125 + | let data = s3
|
126 + | .get_object()
|
127 + | .bucket("test-bucket")
|
128 + | .key("correct-key")
|
129 + | .send()
|
130 + | .await
|
131 + | .expect("success response")
|
132 + | .body
|
133 + | .collect()
|
134 + | .await
|
135 + | .expect("successful read")
|
136 + | .to_vec();
|
137 + |
|
138 + | assert_eq!(data, b"test-test-test");
|
139 + | assert_eq!(2, rule.num_calls());
|
140 + | }
|
141 + |
|
142 + | #[tokio::test]
|
143 + | async fn test_mock_client_retries() {
|
144 + | let rule = mock!(aws_sdk_s3::Client::get_object)
|
145 + | .sequence()
|
146 + | .http_status(503, None)
|
147 + | .times(2)
|
148 + | .output(|| {
|
149 + | GetObjectOutput::builder()
|
150 + | .body(ByteStream::from_static(b"test-test-test"))
|
151 + | .build()
|
152 + | })
|
153 + | .build();
|
154 + |
|
155 + | // test client builder override
|
156 + | let s3 = mock_client!(
|
157 + | aws_sdk_s3,
|
158 + | RuleMode::Sequential,
|
159 + | [&rule],
|
160 + | |client_builder| {
|
161 + | client_builder.retry_config(RetryConfig::standard().with_max_attempts(3))
|
162 + | }
|
163 + | );
|
164 + |
|
165 + | let data = s3
|
166 + | .get_object()
|
167 + | .bucket("test-bucket")
|
168 + | .key("correct-key")
|
169 + | .send()
|
170 + | .await
|
171 + | .expect("success response")
|
172 + | .body
|
173 + | .collect()
|
174 + | .await
|
175 + | .expect("successful read")
|
176 + | .to_vec();
|
177 + |
|
178 + | assert_eq!(data, b"test-test-test");
|
179 + | assert_eq!(3, rule.num_calls());
|
180 + | }
|