Add/alternative runtime tests (#1575)

* add: alternative runtime tests
This commit is contained in:
Zelda Hessler 2022-07-27 10:34:01 -05:00 committed by GitHub
parent d952ccd8f3
commit c7949be857
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 181 additions and 9 deletions

View File

@ -82,7 +82,7 @@ class IntegrationTestDependencies(
private fun serviceSpecificCustomizations(): List<LibRsCustomization> = when (moduleName) {
"transcribestreaming" -> listOf(TranscribeTestDependencies())
"s3" -> listOf(S3TestDependencies())
"s3" -> listOf(S3TestDependencies(runtimeConfig))
else -> emptyList()
}
}
@ -95,19 +95,29 @@ class TranscribeTestDependencies : LibRsCustomization() {
}
}
class S3TestDependencies : LibRsCustomization() {
class S3TestDependencies(
private val runtimeConfig: RuntimeConfig
) : LibRsCustomization() {
override fun section(section: LibRsSection): Writable = writable {
addDependency(AsyncStd)
addDependency(BytesUtils)
addDependency(Smol)
addDependency(TempFile)
runtimeConfig.runtimeCrate("async", scope = DependencyScope.Dev)
runtimeConfig.runtimeCrate("client", scope = DependencyScope.Dev)
runtimeConfig.runtimeCrate("http", scope = DependencyScope.Dev)
runtimeConfig.runtimeCrate("types", scope = DependencyScope.Dev)
}
}
private val AsyncStd = CargoDependency("async-std", CratesIo("1.12"), scope = DependencyScope.Dev)
private val AsyncStream = CargoDependency("async-stream", CratesIo("0.3"), DependencyScope.Dev)
private val Criterion = CargoDependency("criterion", CratesIo("0.3"), scope = DependencyScope.Dev)
private val FuturesCore = CargoDependency("futures-core", CratesIo("0.3"), DependencyScope.Dev)
private val FuturesUtil = CargoDependency("futures-util", CratesIo("0.3"), scope = DependencyScope.Dev)
private val Hound = CargoDependency("hound", CratesIo("3.4"), DependencyScope.Dev)
private val SerdeJson = CargoDependency("serde_json", CratesIo("1"), features = emptySet(), scope = DependencyScope.Dev)
private val Smol = CargoDependency("smol", CratesIo("1.2"), scope = DependencyScope.Dev)
private val Tokio = CargoDependency("tokio", CratesIo("1"), features = setOf("macros", "test-util"), scope = DependencyScope.Dev)
private val FuturesUtil = CargoDependency("futures-util", CratesIo("0.3"), scope = DependencyScope.Dev)
private val Tracing = CargoDependency("tracing", CratesIo("0.1"), scope = DependencyScope.Dev)
private val TracingSubscriber = CargoDependency("tracing-subscriber", CratesIo("0.2"), scope = DependencyScope.Dev)

View File

@ -8,8 +8,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies]
async-std = "1.12"
aws-config = { path = "../../build/aws-sdk/sdk/aws-config" }
aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3" }
aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts" }
aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["rt-tokio"] }
@ -24,6 +26,7 @@ http-body = "0.4.5"
hyper = "0.14"
serde_json = "1"
tempfile = "3"
smol = "1.2"
tokio = { version = "1", features = ["full", "test-util"] }
tracing-subscriber = { version = "0.3.5", features = ["env-filter"] }
tracing = "0.1"

View File

@ -0,0 +1,159 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
use aws_sdk_s3::model::{
CompressionType, CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization,
OutputSerialization,
};
use aws_sdk_s3::{Client, Config, Credentials, Region};
use aws_smithy_async::assert_elapsed;
use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep};
use aws_smithy_client::never::NeverConnector;
use aws_smithy_http::result::SdkError;
use aws_smithy_types::timeout;
use aws_smithy_types::tristate::TriState;
use std::fmt::Debug;
use std::sync::Arc;
use std::time::Duration;
#[derive(Debug)]
struct SmolSleep;
impl AsyncSleep for SmolSleep {
fn sleep(&self, duration: Duration) -> Sleep {
Sleep::new(async move {
smol::Timer::after(duration).await;
})
}
}
#[test]
fn test_smol_runtime_timeouts() {
if let Err(err) = smol::block_on(async { timeout_test(Arc::new(SmolSleep)).await }) {
println!("{err}");
panic!();
}
}
#[test]
fn test_smol_runtime_retry() {
if let Err(err) = smol::block_on(async { retry_test(Arc::new(SmolSleep)).await }) {
println!("{err}");
panic!();
}
}
#[derive(Debug)]
struct AsyncStdSleep;
impl AsyncSleep for AsyncStdSleep {
fn sleep(&self, duration: Duration) -> Sleep {
Sleep::new(async move { async_std::task::sleep(duration).await })
}
}
#[test]
fn test_async_std_runtime_timeouts() {
if let Err(err) =
async_std::task::block_on(async { timeout_test(Arc::new(AsyncStdSleep)).await })
{
println!("{err}");
panic!();
}
}
#[test]
fn test_async_std_runtime_retry() {
if let Err(err) = async_std::task::block_on(async { retry_test(Arc::new(AsyncStdSleep)).await })
{
println!("{err}");
panic!();
}
}
async fn timeout_test(sleep_impl: Arc<dyn AsyncSleep>) -> Result<(), Box<dyn std::error::Error>> {
let conn = NeverConnector::new();
let region = Region::from_static("us-east-2");
let credentials = Credentials::new("test", "test", None, None, "test");
let api_timeouts =
timeout::Api::new().with_call_timeout(TriState::Set(Duration::from_secs_f32(0.5)));
let timeout_config = timeout::Config::new().with_api_timeouts(api_timeouts);
let config = Config::builder()
.region(region)
.credentials_provider(credentials)
.timeout_config(timeout_config)
.sleep_impl(sleep_impl)
.build();
let client = Client::from_conf_conn(config, conn.clone());
let now = std::time::Instant::now();
let err = client
.select_object_content()
.bucket("aws-rust-sdk")
.key("sample_data.csv")
.expression_type(ExpressionType::Sql)
.expression("SELECT * FROM s3object s WHERE s.\"Name\" = 'Jane'")
.input_serialization(
InputSerialization::builder()
.csv(
CsvInput::builder()
.file_header_info(FileHeaderInfo::Use)
.build(),
)
.compression_type(CompressionType::None)
.build(),
)
.output_serialization(
OutputSerialization::builder()
.csv(CsvOutput::builder().build())
.build(),
)
.send()
.await
.unwrap_err();
assert_eq!(format!("{:?}", err), "TimeoutError(RequestTimeoutError { kind: \"API call (all attempts including retries)\", duration: 500ms })");
assert_elapsed!(now, std::time::Duration::from_secs_f32(0.5));
Ok(())
}
async fn retry_test(sleep_impl: Arc<dyn AsyncSleep>) -> Result<(), Box<dyn std::error::Error>> {
let conn = NeverConnector::new();
let credentials = Credentials::new("test", "test", None, None, "test");
let conf = aws_types::SdkConfig::builder()
.region(Region::new("us-east-2"))
.credentials_provider(aws_types::credentials::SharedCredentialsProvider::new(
credentials,
))
.timeout_config(
timeout::Config::new().with_api_timeouts(
timeout::Api::new()
.with_call_attempt_timeout(TriState::Set(Duration::from_secs_f64(0.1))),
),
)
.sleep_impl(sleep_impl)
.build();
let client = Client::from_conf_conn(Config::new(&conf), conn.clone());
let resp = client
.list_buckets()
.send()
.await
.expect_err("call should fail");
assert_eq!(
conn.num_calls(),
3,
"client level timeouts should be retried"
);
assert!(
matches!(resp, SdkError::TimeoutError { .. }),
"expected a timeout error, got: {}",
resp
);
Ok(())
}

View File

@ -19,7 +19,7 @@ use aws_smithy_types::tristate::TriState;
use std::sync::Arc;
use std::time::Duration;
#[tokio::test]
#[tokio::test(start_paused = true)]
async fn test_timeout_service_ends_request_that_never_completes() {
let conn: NeverService<http::Request<SdkBody>, http::Response<SdkBody>, ConnectorError> =
NeverService::new();
@ -38,7 +38,6 @@ async fn test_timeout_service_ends_request_that_never_completes() {
let client = Client::from_conf_conn(config, conn.clone());
let now = tokio::time::Instant::now();
tokio::time::pause();
let err = client
.select_object_content()

View File

@ -217,7 +217,7 @@ data class CargoDependency(
fun SmithyHttp(runtimeConfig: RuntimeConfig) = runtimeConfig.runtimeCrate("http")
fun SmithyHttpTower(runtimeConfig: RuntimeConfig) = runtimeConfig.runtimeCrate("http-tower")
fun SmithyProtocolTestHelpers(runtimeConfig: RuntimeConfig) =
runtimeConfig.runtimeCrate("protocol-test").copy(scope = DependencyScope.Dev)
runtimeConfig.runtimeCrate("protocol-test", scope = DependencyScope.Dev)
fun smithyJson(runtimeConfig: RuntimeConfig): CargoDependency = runtimeConfig.runtimeCrate("json")
fun smithyQuery(runtimeConfig: RuntimeConfig): CargoDependency = runtimeConfig.runtimeCrate("query")
fun smithyXml(runtimeConfig: RuntimeConfig): CargoDependency = runtimeConfig.runtimeCrate("xml")

View File

@ -12,6 +12,7 @@ import software.amazon.smithy.model.traits.TimestampFormatTrait
import software.amazon.smithy.rust.codegen.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.rustlang.CratesIo
import software.amazon.smithy.rust.codegen.rustlang.DependencyLocation
import software.amazon.smithy.rust.codegen.rustlang.DependencyScope
import software.amazon.smithy.rust.codegen.rustlang.InlineDependency
import software.amazon.smithy.rust.codegen.rustlang.Local
import software.amazon.smithy.rust.codegen.rustlang.RustDependency
@ -87,8 +88,8 @@ data class RuntimeConfig(
val crateSrcPrefix: String = cratePrefix.replace("-", "_")
fun runtimeCrate(runtimeCrateName: String, optional: Boolean = false): CargoDependency =
CargoDependency("$cratePrefix-$runtimeCrateName", runtimeCrateLocation.crateLocation(), optional = optional)
fun runtimeCrate(runtimeCrateName: String, optional: Boolean = false, scope: DependencyScope = DependencyScope.Compile): CargoDependency =
CargoDependency("$cratePrefix-$runtimeCrateName", runtimeCrateLocation.crateLocation(), optional = optional, scope = scope)
}
/**

View File

@ -14,7 +14,7 @@ use std::task::{Context, Poll};
use std::time::Duration;
/// Async trait with a `sleep` function.
pub trait AsyncSleep: std::fmt::Debug + Send + Sync {
pub trait AsyncSleep: Debug + Send + Sync {
/// Returns a future that sleeps for the given `duration` of time.
fn sleep(&self, duration: Duration) -> Sleep;
}