Skip to content

Commit

Permalink
Rust TestConnectors (awsdocs#4151)
Browse files Browse the repository at this point in the history
* Mock connector tests

* Add single_shot macros for one-time request and response testing clients.

* Use single_shot for iam tests

* Moved mock connector macros to their own crate

* Responding to feedback

* Update without from_conf_conn

* Bump deps versions

* Use new credentials crate

* Clippy

* Editorial

Co-authored-by: Steven Meyer <[email protected]>
  • Loading branch information
DavidSouther and meyertst-aws authored Jan 10, 2023
1 parent cb42132 commit c8e8df4
Show file tree
Hide file tree
Showing 16 changed files with 329 additions and 45 deletions.
1 change: 1 addition & 0 deletions rust_dev_preview/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
1 change: 1 addition & 0 deletions rust_dev_preview/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ members = [
"sqs",
"ssm",
"stepfunction",
"test-utils",
"testing",
"tls",
"transcribestreaming",
Expand Down
5 changes: 5 additions & 0 deletions rust_dev_preview/cross_service/rest_ses/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ name = "rest_ses"
actix-web = "4"
actix-web-prom = "0.6.0"
aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-sdk-cloudwatchlogs = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-sdk-rdsdata = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-sdk-ses = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
chrono = { version = "0.4.22", default-features = false, features = [
Expand All @@ -24,9 +25,12 @@ chrono = { version = "0.4.22", default-features = false, features = [
color-eyre = "0.6.2"
config = "0.13.2"
derive_more = "0.99.17"
futures = "0.3.24"
http = "0.2.8"
# newer versions of this crate aren't yet compatible with our MSRV
mail-builder = "=0.2.2"
reqwest = "0.11.12"
sdk-examples-test-utils = { path = "../../test-utils" }
secrecy = { version = "0.8.0", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1.0.86"
Expand All @@ -41,6 +45,7 @@ uuid = { version = "1.2.1", features = ["v4", "serde"] }
xlsxwriter = "0.3.1"

[dev-dependencies]
aws-smithy-client = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next", features = ["test-util"] }
aws-smithy-http = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
fake = { version = "2.5.0", features = ["uuid"] }
once_cell = "1.15.0"
Expand Down
29 changes: 29 additions & 0 deletions rust_dev_preview/cross_service/rest_ses/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,35 @@ impl RdsClient {
}
}

#[cfg(test)]
mod rds_client_for_test {
use aws_smithy_http::body::SdkBody;
use secrecy::Secret;

use super::RdsClient;
impl RdsClient {
pub fn for_test(
pairs: Vec<(
http::request::Request<SdkBody>,
http::response::Response<SdkBody>,
)>,
) -> Self {
RdsClient {
client: aws_sdk_rdsdata::Client::from_conf(
sdk_examples_test_utils::client_config!(aws_sdk_rdsdata)
.http_connector(aws_smithy_client::test_connection::TestConnection::new(
pairs,
))
.build(),
),
secret_arn: Secret::from("secret".to_string()),
cluster_arn: "cluster".into(),
db_instance: "db".into(),
}
}
}
}

/// A newtype wrapper for Email addresses.
#[derive(Debug, Clone, Deserialize)]
pub struct Email(String);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,21 @@ fn parse_rds_output(
))),
}
}

#[cfg(test)]
mod test {
use crate::{client::RdsClient, work_item::WorkItem};

use super::create;
use sdk_examples_test_utils::test_event;

#[tokio::test]
async fn test_create_failed() {
let item: WorkItem = Default::default();
let client = RdsClient::for_test(vec![test_event!("", (400, ""))]);

let result = create(item, &client).await;

assert!(result.is_err());
}
}
19 changes: 12 additions & 7 deletions rust_dev_preview/iam/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@ name = "iam_getting_started"
path = "src/bin/iam-getting-started.rs"

[dependencies]
aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
aws-sdk-iam = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
aws-sdk-s3 = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
aws-sdk-sts = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
aws-types = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main", features = [
"hardcoded-credentials",
] }
aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-credential-types = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next", features = ["hardcoded-credentials"] }
aws-hyper = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-sdk-iam = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-sdk-s3 = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-sdk-sts = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-smithy-client = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next", features = ["test-util"] }
aws-smithy-http = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-types = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
sdk-examples-test-utils = { path = "../test-utils" }
tokio = { version = "1.20.1", features = ["full"] }
structopt = { version = "0.3", default-features = false }
tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }
uuid = { version = "0.8", features = ["serde", "v4"] }
tower-service = "0.3.2"
http = "0.2.8"
50 changes: 37 additions & 13 deletions rust_dev_preview/iam/src/bin/create-role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,23 @@ struct Opt {

// Creates a role.
// snippet-start:[iam.rust.create-role]
async fn make_role(client: &Client, policy_file: &str, name: &str) -> Result<(), Error> {
// Read policy doc from file as a string
let doc = fs::read_to_string(policy_file).expect("Unable to read file");

async fn make_role(client: &Client, policy: &str, name: &str) -> String {
let resp = client
.create_role()
.assume_role_policy_document(doc)
.assume_role_policy_document(policy)
.role_name(name)
.send()
.await?;
.await;

println!(
"Created role with ARN {}",
resp.role().unwrap().arn().unwrap()
);

Ok(())
match resp {
Ok(output) => {
format!(
"Created role with ARN {}",
output.role().unwrap().arn().unwrap()
)
}
Err(err) => format!("Error creating role: {:?}", err),
}
}
// snippet-end:[iam.rust.create-role]

Expand Down Expand Up @@ -90,5 +90,29 @@ async fn main() -> Result<(), Error> {
let shared_config = aws_config::from_env().region(region_provider).load().await;
let client = Client::new(&shared_config);

make_role(&client, &policy_file, &name).await
// Read policy doc from file as a string
let policy_doc = fs::read_to_string(policy_file).expect("Unable to read file");

let response = make_role(&client, policy_doc.as_str(), &name).await;
println!("{response}");

Ok(())
}

#[cfg(test)]
mod test {
use crate::make_role;

#[tokio::test]
async fn test_make_role() {
let client = sdk_examples_test_utils::single_shot_client!(
sdk: aws_sdk_iam,
request: "request body",
status: 500,
response: "error body"
);

let response = make_role(&client, "{}".into(), "test_role".into()).await;
assert!(response.starts_with("Error creating role: "));
}
}
53 changes: 39 additions & 14 deletions rust_dev_preview/iam/src/iam-service-lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,37 @@ pub async fn create_policy(
}
// snippet-end:[rust.example_code.iam.service.create_policy]

#[cfg(test)]
mod test_create_policy {
use crate::create_policy;
use http::StatusCode;
use sdk_examples_test_utils::single_shot_client;

#[tokio::test]
async fn test_create_policy_success() {
let client = single_shot_client!(
sdk: aws_sdk_iam,
status: StatusCode::OK,
response: include_str!("../testing/test_create_policy_response_success.xml")
);

let response = create_policy(&client, "{}".into(), "test_role".into()).await;
assert!(response.is_ok());
}

#[tokio::test]
async fn test_create_policy_failed() {
let client = single_shot_client!(
sdk: aws_sdk_iam,
status: StatusCode::BAD_REQUEST,
response: include_str!("../testing/test_create_policy_response_malformed.xml")
);

let response = create_policy(&client, "{}".into(), "test_role".into()).await;
assert!(response.is_err());
}
}

// snippet-start:[rust.example_code.iam.service.create_role]
pub async fn create_role(
client: &iamClient,
Expand Down Expand Up @@ -137,20 +168,14 @@ pub async fn create_user_policy(
// snippet-start:[rust.example_code.iam.service.delete_role]
pub async fn delete_role(client: &iamClient, role: &Role) -> Result<(), iamError> {
let role = role.clone();
loop {
match client
.delete_role()
.role_name(role.role_name.as_ref().unwrap())
.send()
.await
{
Ok(_) => {
break;
}
Err(_) => {
sleep(Duration::from_secs(2)).await;
}
}
while client
.delete_role()
.role_name(role.role_name.as_ref().unwrap())
.send()
.await
.is_err()
{
sleep(Duration::from_secs(2)).await;
}
Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>MalformedPolicyDocument</Code>
<Message>MalformedPolicyDocument</Message>
<RequestId>ABC123EXAMPLE</RequestId>
<HostId>RVhBTVBMRSBIT1NU</HostId>
</Error>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<CreatePolicyResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<CreatePolicyResult>
<Policy>
<PolicyName>S3-read-only-example-bucket</PolicyName>
<DefaultVersionId>v1</DefaultVersionId>
<PolicyId>AGPACKCEVSQ6C2EXAMPLE</PolicyId>
<Path>/</Path>
<Arn>arn:aws:iam::123456789012:policy/S3-read-only-example-bucket</Arn>
<AttachmentCount>0</AttachmentCount>
<CreateDate>2014-09-15T17:36:14.673Z</CreateDate>
<UpdateDate>2014-09-15T17:36:14.673Z</UpdateDate>
</Policy>
</CreatePolicyResult>
<ResponseMetadata>
<RequestId>ca64c9e1-3cfe-11e4-bfad-8d1c6EXAMPLE</RequestId>
</ResponseMetadata>
</CreatePolicyResponse>
19 changes: 19 additions & 0 deletions rust_dev_preview/test-utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "sdk-examples-test-utils"
version = "0.1.0"
authors = [
"David Souther <[email protected]>",
]
edition = "2021"

[dependencies]
aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-smithy-client = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next", features = [
"test-util",
] }
aws-smithy-http = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next" }
aws-types = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "next"}
http = "0.2"

[lib]
path="src/mod.rs"
39 changes: 39 additions & 0 deletions rust_dev_preview/test-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Test Utilities for SDK for Rust code examples

## Purpose

These utilities help streamline common testing actions in the AWS SDK for Rust.

## Code example

- [Macros for creating mock connection request/response pairs](src/macros.rs)

## ⚠ Important

- We recommend that you grant this code least privilege,
or at most the minimum permissions required to perform the task.
For more information, see
[Grant Least Privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege)
in the AWS Identity and Access Management User Guide.
- This code has not been tested in all AWS Regions.
Some AWS services are available only in specific
[Regions](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
- Running this code might result in charges to your AWS account.

## Running the code example

The code in this example is not self-executing. Instead, look at its usage in
the unit tests of other AWS SDK for Rust examples.

## Resources

- [AWS SDK for Rust repo](https://github.com/awslabs/aws-sdk-rust)
- [AWS SDK for Rust Developer Guide](https://docs.aws.amazon.com/sdk-for-rust/latest/dg)

## Contributing

To propose a new code example to the AWS documentation team,
see [CONTRIBUTING.md](https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/CONTRIBUTING.md).
The team prefers to create code examples that show broad scenarios rather than individual API calls.

Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0
Loading

0 comments on commit c8e8df4

Please sign in to comment.