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

Add/alternative runtime tests (#1575)

* add: alternative runtime tests
parent d952ccd8
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -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)
+3 −0
Original line number Diff line number Diff line
@@ -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"
+159 −0
Original line number Diff line number Diff line
/*
 * 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(())
}
+1 −2
Original line number Diff line number Diff line
@@ -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()
+1 −1
Original line number Diff line number Diff line
@@ -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")
Loading