Unverified Commit 2d931fa8 authored by Zelda Hessler's avatar Zelda Hessler Committed by GitHub
Browse files

Fix the runtime versioner and update tool deps (#3503)

This issue was caused by a runtime crate referencing another runtime
crate with a version dependency instead of a path dependency. I've added
an error message that tells us how to fix this if it comes up again:

```txt
crate `aws-smithy-mocks-experimental` depends on crate `aws-smithy-types` crate by version instead of by path. Please update it to use path dependencies for all runtime crates.
```

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent 9c6f7e64
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -8,8 +8,8 @@ license = "Apache-2.0"
repository = "https://github.com/smithy-lang/smithy-rs"

[dependencies]
aws-smithy-types = "1"
aws-smithy-runtime-api = { version = "1", features = ["client", "http-02x"] }
aws-smithy-types = { path = "../aws-smithy-types" }
aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["client", "http-02x"] }

[dev-dependencies]
aws-sdk-s3 = { version = "1", features = ["test-util"] }
+103 −100
Original line number Diff line number Diff line
@@ -10,103 +10,106 @@ async fn main() {
    // this is an example of writing tests, see the tests
}

use aws_sdk_s3::operation::get_object::GetObjectError;
use aws_sdk_s3::Client;
use std::error::Error;

pub struct MyFileRetriever {
    s3_client: Client,
}

impl MyFileRetriever {
    pub async fn get_file(&self, path: &str) -> Result<Option<String>, Box<dyn Error>> {
        let response = match self
            .s3_client
            .get_object()
            .bucket("test-bucket")
            .key(path)
            .send()
            .await
            .map_err(|e| e.into_service_error())
        {
            Ok(response) => response,
            Err(GetObjectError::NoSuchKey(_)) => return Ok(None),
            e @ Err(_) => e?,
        };
        let contents = response.body.collect().await?.to_vec();
        let contents = String::from_utf8(contents)?;
        Ok(Some(contents))
    }
}

// intentionally not cfg(test) so that rustdoc can find this
mod test {
    use aws_sdk_s3::config::Region;
    use aws_sdk_s3::operation::get_object::{GetObjectError, GetObjectOutput};
    use aws_sdk_s3::types::error::NoSuchKey;
    use aws_sdk_s3::Client;
    use aws_smithy_mocks_experimental::{mock, MockResponseInterceptor};
    use aws_smithy_runtime_api::client::orchestrator::HttpResponse;
    use aws_smithy_runtime_api::http::StatusCode;
    use aws_smithy_types::body::SdkBody;
    use aws_smithy_types::byte_stream::ByteStream;

    #[allow(dead_code)]
    fn mocked_client(file_contents: impl AsRef<[u8]>) -> Client {
        let file_contents = file_contents.as_ref().to_vec();
        let get_object_happy_path = mock!(Client::get_object)
            .match_requests(|req| {
                req.bucket() == Some("test-bucket") && req.key() == Some("test-key")
            })
            .then_output(move || {
                GetObjectOutput::builder()
                    .body(ByteStream::from(file_contents.clone()))
                    .build()
            });
        // fallback error
        let get_object_error_path = mock!(Client::get_object)
            .then_error(|| GetObjectError::NoSuchKey(NoSuchKey::builder().build()));
        let hinted_500_error = mock!(Client::get_object)
            .match_requests(|req| req.key() == Some("500"))
            .then_http_response(|| {
                HttpResponse::new(
                    StatusCode::try_from(500).unwrap(),
                    SdkBody::from("internal server error"),
                )
            });
        let mock_response_interceptor = MockResponseInterceptor::new()
            .with_rule(&get_object_happy_path)
            .with_rule(&hinted_500_error)
            .with_rule(&get_object_error_path);
        Client::from_conf(
            aws_sdk_s3::Config::builder()
                .with_test_defaults()
                .region(Region::from_static("us-east-1"))
                .interceptor(mock_response_interceptor)
                .build(),
        )
    }

    #[tokio::test]
    async fn loads_file() {
        let client = super::MyFileRetriever {
            s3_client: mocked_client(b"12345-abcde"),
        };
        assert_eq!(
            client.get_file("test-key").await.unwrap().as_deref(),
            Some("12345-abcde")
        );
        assert_eq!(client.get_file("different-key").await.unwrap(), None)
    }

    #[tokio::test]
    async fn returns_error_on_invalid_utf8() {
        let client = super::MyFileRetriever {
            s3_client: mocked_client(&vec![0xFF, 0xFE]),
        };
        client
            .get_file("test-key")
            .await
            .expect_err("invalid UTF-8");
    }
}
// TODO(fix-aws-smithy-mocks-experimental) This is currently broken because it
//     depends on a generated crate examples must have a `main()` fn so it was
//     easier to comment out the code below rather than cfg-gate it.
// use aws_sdk_s3::operation::get_object::GetObjectError;
// use aws_sdk_s3::Client;
// use std::error::Error;
//
// pub struct MyFileRetriever {
//     s3_client: Client,
// }
//
// impl MyFileRetriever {
//     pub async fn get_file(&self, path: &str) -> Result<Option<String>, Box<dyn Error>> {
//         let response = match self
//             .s3_client
//             .get_object()
//             .bucket("test-bucket")
//             .key(path)
//             .send()
//             .await
//             .map_err(|e| e.into_service_error())
//         {
//             Ok(response) => response,
//             Err(GetObjectError::NoSuchKey(_)) => return Ok(None),
//             e @ Err(_) => e?,
//         };
//         let contents = response.body.collect().await?.to_vec();
//         let contents = String::from_utf8(contents)?;
//         Ok(Some(contents))
//     }
// }
//
// // intentionally not cfg(test) so that rustdoc can find this
// mod test {
//     use aws_sdk_s3::config::Region;
//     use aws_sdk_s3::operation::get_object::{GetObjectError, GetObjectOutput};
//     use aws_sdk_s3::types::error::NoSuchKey;
//     use aws_sdk_s3::Client;
//     use aws_smithy_mocks_experimental::{mock, MockResponseInterceptor};
//     use aws_smithy_runtime_api::client::orchestrator::HttpResponse;
//     use aws_smithy_runtime_api::http::StatusCode;
//     use aws_smithy_types::body::SdkBody;
//     use aws_smithy_types::byte_stream::ByteStream;
//
//     #[allow(dead_code)]
//     fn mocked_client(file_contents: impl AsRef<[u8]>) -> Client {
//         let file_contents = file_contents.as_ref().to_vec();
//         let get_object_happy_path = mock!(Client::get_object)
//             .match_requests(|req| {
//                 req.bucket() == Some("test-bucket") && req.key() == Some("test-key")
//             })
//             .then_output(move || {
//                 GetObjectOutput::builder()
//                     .body(ByteStream::from(file_contents.clone()))
//                     .build()
//             });
//         // fallback error
//         let get_object_error_path = mock!(Client::get_object)
//             .then_error(|| GetObjectError::NoSuchKey(NoSuchKey::builder().build()));
//         let hinted_500_error = mock!(Client::get_object)
//             .match_requests(|req| req.key() == Some("500"))
//             .then_http_response(|| {
//                 HttpResponse::new(
//                     StatusCode::try_from(500).unwrap(),
//                     SdkBody::from("internal server error"),
//                 )
//             });
//         let mock_response_interceptor = MockResponseInterceptor::new()
//             .with_rule(&get_object_happy_path)
//             .with_rule(&hinted_500_error)
//             .with_rule(&get_object_error_path);
//         Client::from_conf(
//             aws_sdk_s3::Config::builder()
//                 .with_test_defaults()
//                 .region(Region::from_static("us-east-1"))
//                 .interceptor(mock_response_interceptor)
//                 .build(),
//         )
//     }
//
//     #[tokio::test]
//     async fn loads_file() {
//         let client = super::MyFileRetriever {
//             s3_client: mocked_client(b"12345-abcde"),
//         };
//         assert_eq!(
//             client.get_file("test-key").await.unwrap().as_deref(),
//             Some("12345-abcde")
//         );
//         assert_eq!(client.get_file("different-key").await.unwrap(), None)
//     }
//
//     #[tokio::test]
//     async fn returns_error_on_invalid_utf8() {
//         let client = super::MyFileRetriever {
//             s3_client: mocked_client(&vec![0xFF, 0xFE]),
//         };
//         client
//             .get_file("test-key")
//             .await
//             .expect_err("invalid UTF-8");
//     }
// }
+3 −3
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
///
/// # Examples
/// **Mock and return a success response**:
/// ```rust
/// ```rust,ignore
/// use aws_sdk_s3::operation::get_object::GetObjectOutput;
/// use aws_sdk_s3::Client;
/// use aws_smithy_types::byte_stream::ByteStream;
@@ -49,7 +49,7 @@ use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
/// ```
///
/// **Mock and return an error**:
/// ```rust
/// ```rust,ignore
/// use aws_sdk_s3::operation::get_object::GetObjectError;
/// use aws_sdk_s3::types::error::NoSuchKey;
/// use aws_sdk_s3::Client;
@@ -81,7 +81,7 @@ macro_rules! mock {
///
/// # Examples
/// **Create a client that uses a mock failure and then a success**:
/// ```rust
/// ```rust,ignore
/// use aws_sdk_s3::operation::get_object::{GetObjectOutput, GetObjectError};
/// use aws_sdk_s3::types::error::NoSuchKey;
/// use aws_sdk_s3::Client;
+141 −139
Original line number Diff line number Diff line
@@ -3,142 +3,144 @@
 * SPDX-License-Identifier: Apache-2.0
 */

use aws_sdk_s3::config::Region;
use aws_sdk_s3::operation::get_object::{GetObjectError, GetObjectOutput};
use aws_sdk_s3::operation::list_buckets::ListBucketsError;
use aws_sdk_s3::{Client, Config};
use aws_smithy_runtime_api::client::orchestrator::HttpResponse;
use aws_smithy_runtime_api::http::StatusCode;
use aws_smithy_types::body::SdkBody;
use aws_smithy_types::byte_stream::ByteStream;
use aws_smithy_types::error::metadata::ProvideErrorMetadata;
use aws_smithy_types::error::ErrorMetadata;

use aws_smithy_mocks_experimental::{mock, mock_client, MockResponseInterceptor, RuleMode};

const S3_NO_SUCH_KEY: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>NoSuchKey</Code>
  <Message>The resource you requested does not exist</Message>
  <Resource>/mybucket/myfoto.jpg</Resource>
  <RequestId>4442587FB7D0A2F9</RequestId>
</Error>"#;

#[tokio::test]
async fn create_mock_s3_get_object() {
    let s3_404 = mock!(Client::get_object)
        .match_requests(|inp| {
            inp.bucket() == Some("test-bucket") && inp.key() != Some("correct-key")
        })
        .then_http_response(|| {
            HttpResponse::new(
                StatusCode::try_from(400).unwrap(),
                SdkBody::from(S3_NO_SUCH_KEY),
            )
        });

    let s3_real_object = mock!(Client::get_object)
        .match_requests(|inp| {
            inp.bucket() == Some("test-bucket") && inp.key() == Some("correct-key")
        })
        .then_output(|| {
            GetObjectOutput::builder()
                .body(ByteStream::from_static(b"test-test-test"))
                .build()
        });

    let modeled_error = mock!(Client::list_buckets).then_error(|| {
        ListBucketsError::generic(ErrorMetadata::builder().code("InvalidAccessKey").build())
    });

    let get_object_mocks = MockResponseInterceptor::new()
        .rule_mode(RuleMode::Sequential)
        .with_rule(&s3_404)
        .with_rule(&s3_real_object)
        .with_rule(&modeled_error);

    let s3 = aws_sdk_s3::Client::from_conf(
        Config::builder()
            .with_test_defaults()
            .region(Region::new("us-east-1"))
            .interceptor(get_object_mocks)
            .build(),
    );

    let error = s3
        .get_object()
        .bucket("test-bucket")
        .key("foo")
        .send()
        .await
        .expect_err("404");
    assert!(matches!(
        error.into_service_error(),
        GetObjectError::NoSuchKey(_)
    ));
    assert_eq!(s3_404.num_calls(), 1);

    let data = s3
        .get_object()
        .bucket("test-bucket")
        .key("correct-key")
        .send()
        .await
        .expect("success response")
        .body
        .collect()
        .await
        .expect("successful read")
        .to_vec();
    assert_eq!(data, b"test-test-test");
    assert_eq!(s3_real_object.num_calls(), 1);

    let err = s3.list_buckets().send().await.expect_err("bad access key");
    assert_eq!(err.code(), Some("InvalidAccessKey"));
}

#[tokio::test]
async fn mock_client() {
    let s3_404 = mock!(Client::get_object).then_http_response(|| {
        HttpResponse::new(
            StatusCode::try_from(400).unwrap(),
            SdkBody::from(S3_NO_SUCH_KEY),
        )
    });

    let s3_real_object = mock!(Client::get_object).then_output(|| {
        GetObjectOutput::builder()
            .body(ByteStream::from_static(b"test-test-test"))
            .build()
    });

    let s3 = mock_client!(aws_sdk_s3, [&s3_404, &s3_real_object]);

    let error = s3
        .get_object()
        .bucket("test-bucket")
        .key("foo")
        .send()
        .await
        .expect_err("404");
    assert!(matches!(
        error.into_service_error(),
        GetObjectError::NoSuchKey(_)
    ));
    assert_eq!(s3_404.num_calls(), 1);

    let data = s3
        .get_object()
        .bucket("test-bucket")
        .key("correct-key")
        .send()
        .await
        .expect("success response")
        .body
        .collect()
        .await
        .expect("successful read")
        .to_vec();
    assert_eq!(data, b"test-test-test");
    assert_eq!(s3_real_object.num_calls(), 1);
}
// TODO(fix-aws-smithy-mocks-experimental) This is currently broken because it depends on a generated crate

// use aws_sdk_s3::config::Region;
// use aws_sdk_s3::operation::get_object::{GetObjectError, GetObjectOutput};
// use aws_sdk_s3::operation::list_buckets::ListBucketsError;
// use aws_sdk_s3::{Client, Config};
// use aws_smithy_runtime_api::client::orchestrator::HttpResponse;
// use aws_smithy_runtime_api::http::StatusCode;
// use aws_smithy_types::body::SdkBody;
// use aws_smithy_types::byte_stream::ByteStream;
// use aws_smithy_types::error::metadata::ProvideErrorMetadata;
// use aws_smithy_types::error::ErrorMetadata;
//
// use aws_smithy_mocks_experimental::{mock, mock_client, MockResponseInterceptor, RuleMode};
//
// const S3_NO_SUCH_KEY: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
// <Error>
//   <Code>NoSuchKey</Code>
//   <Message>The resource you requested does not exist</Message>
//   <Resource>/mybucket/myfoto.jpg</Resource>
//   <RequestId>4442587FB7D0A2F9</RequestId>
// </Error>"#;
//
// #[tokio::test]
// async fn create_mock_s3_get_object() {
//     let s3_404 = mock!(Client::get_object)
//         .match_requests(|inp| {
//             inp.bucket() == Some("test-bucket") && inp.key() != Some("correct-key")
//         })
//         .then_http_response(|| {
//             HttpResponse::new(
//                 StatusCode::try_from(400).unwrap(),
//                 SdkBody::from(S3_NO_SUCH_KEY),
//             )
//         });
//
//     let s3_real_object = mock!(Client::get_object)
//         .match_requests(|inp| {
//             inp.bucket() == Some("test-bucket") && inp.key() == Some("correct-key")
//         })
//         .then_output(|| {
//             GetObjectOutput::builder()
//                 .body(ByteStream::from_static(b"test-test-test"))
//                 .build()
//         });
//
//     let modeled_error = mock!(Client::list_buckets).then_error(|| {
//         ListBucketsError::generic(ErrorMetadata::builder().code("InvalidAccessKey").build())
//     });
//
//     let get_object_mocks = MockResponseInterceptor::new()
//         .rule_mode(RuleMode::Sequential)
//         .with_rule(&s3_404)
//         .with_rule(&s3_real_object)
//         .with_rule(&modeled_error);
//
//     let s3 = aws_sdk_s3::Client::from_conf(
//         Config::builder()
//             .with_test_defaults()
//             .region(Region::new("us-east-1"))
//             .interceptor(get_object_mocks)
//             .build(),
//     );
//
//     let error = s3
//         .get_object()
//         .bucket("test-bucket")
//         .key("foo")
//         .send()
//         .await
//         .expect_err("404");
//     assert!(matches!(
//         error.into_service_error(),
//         GetObjectError::NoSuchKey(_)
//     ));
//     assert_eq!(s3_404.num_calls(), 1);
//
//     let data = s3
//         .get_object()
//         .bucket("test-bucket")
//         .key("correct-key")
//         .send()
//         .await
//         .expect("success response")
//         .body
//         .collect()
//         .await
//         .expect("successful read")
//         .to_vec();
//     assert_eq!(data, b"test-test-test");
//     assert_eq!(s3_real_object.num_calls(), 1);
//
//     let err = s3.list_buckets().send().await.expect_err("bad access key");
//     assert_eq!(err.code(), Some("InvalidAccessKey"));
// }
//
// #[tokio::test]
// async fn mock_client() {
//     let s3_404 = mock!(Client::get_object).then_http_response(|| {
//         HttpResponse::new(
//             StatusCode::try_from(400).unwrap(),
//             SdkBody::from(S3_NO_SUCH_KEY),
//         )
//     });
//
//     let s3_real_object = mock!(Client::get_object).then_output(|| {
//         GetObjectOutput::builder()
//             .body(ByteStream::from_static(b"test-test-test"))
//             .build()
//     });
//
//     let s3 = mock_client!(aws_sdk_s3, [&s3_404, &s3_real_object]);
//
//     let error = s3
//         .get_object()
//         .bucket("test-bucket")
//         .key("foo")
//         .send()
//         .await
//         .expect_err("404");
//     assert!(matches!(
//         error.into_service_error(),
//         GetObjectError::NoSuchKey(_)
//     ));
//     assert_eq!(s3_404.num_calls(), 1);
//
//     let data = s3
//         .get_object()
//         .bucket("test-bucket")
//         .key("correct-key")
//         .send()
//         .await
//         .expect("success response")
//         .body
//         .collect()
//         .await
//         .expect("successful read")
//         .to_vec();
//     assert_eq!(data, b"test-test-test");
//     assert_eq!(s3_real_object.num_calls(), 1);
// }
+1 −1
Original line number Diff line number Diff line
@@ -105,7 +105,7 @@ ARG cargo_hack_version=0.5.23
RUN cargo install cargo-hack --locked --version ${cargo_hack_version}

FROM install_rust AS cargo_minimal_versions
ARG cargo_minimal_versions_version=0.1.19
ARG cargo_minimal_versions_version=0.1.27
RUN cargo install cargo-minimal-versions --locked --version ${cargo_minimal_versions_version}

FROM install_rust AS cargo_check_external_types
Loading