From 901144e7df79e73878edb97b4ffad5ad60e0fccd Mon Sep 17 00:00:00 2001 From: Landon James Date: Wed, 5 Jun 2024 10:58:07 -0700 Subject: [PATCH] Replacing S3 integ test with model test Fixing up isTruncatedPaginator model test --- .../customize/IsTruncatedPaginatorTest.kt | 159 ++++++++++++++++++ .../s3/tests/is-truncated-pagination.rs | 86 ---------- .../generators/PaginatorGeneratorTest.kt | 26 ++- 3 files changed, 184 insertions(+), 87 deletions(-) create mode 100644 aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/customize/IsTruncatedPaginatorTest.kt delete mode 100644 aws/sdk/integration-tests/s3/tests/is-truncated-pagination.rs diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/customize/IsTruncatedPaginatorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/customize/IsTruncatedPaginatorTest.kt new file mode 100644 index 0000000000..4ea0a35756 --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/customize/IsTruncatedPaginatorTest.kt @@ -0,0 +1,159 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk.customize + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.traits.IsTruncatedPaginatorTrait +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.integrationTest +import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rustsdk.AwsRuntimeType +import software.amazon.smithy.rustsdk.awsSdkIntegrationTest + +class IsTruncatedPaginatorTest { + private val model = + """ + namespace test + + use aws.protocols#restXml + use aws.api#service + use smithy.rules#endpointRuleSet + + @restXml + @service(sdkId: "fake") + @endpointRuleSet({ + "version": "1.0", + "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }], + "parameters": { + "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" }, + } + }) + service TestService { + operations: [PaginatedList] + } + + @readonly + @optionalAuth + @http(uri: "/PaginatedList", method: "POST") + @paginated(inputToken: "nextToken", outputToken: "nextToken", + pageSize: "maxResults", items: "items") + operation PaginatedList { + input: GetFoosInput, + output: GetFoosOutput + } + + structure GetFoosInput { + maxResults: Integer, + nextToken: String + } + + structure GetFoosOutput { + nextToken: String, + items: StringList, + isTruncated: Boolean, + } + + list StringList { + member: String + } + """.asSmithyModel() + + @Test + fun `isTruncated paginators work`() { + // Adding IsTruncated trait to the output shape + val modifiedModel = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(shape.isStructureShape && shape.toShapeId() == ShapeId.from("test#GetFoosOutput")) { + (it as StructureShape).toBuilder().addTrait(IsTruncatedPaginatorTrait()).build() + } + } + + awsSdkIntegrationTest(modifiedModel) { context, rustCrate -> + val rc = context.runtimeConfig + val moduleName = context.moduleUseName() + rustCrate.integrationTest("is_truncated_paginator") { + rustTemplate( + """ + ##![cfg(feature = "test-util")] + + use $moduleName::Config; + use $moduleName::Client; + use #{Region}; + use aws_smithy_runtime::client::http::test_util::{ReplayEvent, StaticReplayClient}; + use aws_smithy_types::body::SdkBody; + + fn mk_response(part_marker: u8) -> http::Response { + let (part_num_marker, next_num_marker, is_truncated) = if part_marker < 3 { + (part_marker, part_marker + 1, true) + } else { + (part_marker, 0, false) + }; + let body = format!( + "\n + + {part_num_marker} + {next_num_marker} + {is_truncated} + " + ); + http::Response::builder().body(SdkBody::from(body)).unwrap() + } + + fn mk_request() -> http::Request { + http::Request::builder() + .uri("https://some-test-bucket.s3.us-east-1.amazonaws.com/test.txt?part-number-marker=PartNumberMarker&uploadId=UploadId") + .body(SdkBody::empty()) + .unwrap() + } + + ##[#{tokio}::test] + async fn is_truncated_pagination_does_not_loop() { + let http_client = StaticReplayClient::new(vec![ + ReplayEvent::new(mk_request(), mk_response(0)), + ReplayEvent::new(mk_request(), mk_response(1)), + ReplayEvent::new(mk_request(), mk_response(2)), + ReplayEvent::new(mk_request(), mk_response(3)), + //The events below should never be called because the pagination should + //terminate with the event above + ReplayEvent::new(mk_request(), mk_response(0)), + ReplayEvent::new(mk_request(), mk_response(1)), + ]); + + let config = Config::builder() + .region(Region::new("fake")) + .http_client(http_client.clone()) + .with_test_defaults() + .build(); + let client = Client::from_conf(config); + + let list_parts_res = client + .paginated_list() + .max_results(1) + .into_paginator() + .send() + .collect::>() + .await; + + // Confirm that the pagination stopped calling the http client after the + // first page with is_truncated = false + assert_eq!(list_parts_res.len(), 4) + } + """, + *preludeScope, + "tokio" to CargoDependency.Tokio.toType(), + "Region" to AwsRuntimeType.awsTypes(rc).resolve("region::Region"), + ) + } + } + } +} diff --git a/aws/sdk/integration-tests/s3/tests/is-truncated-pagination.rs b/aws/sdk/integration-tests/s3/tests/is-truncated-pagination.rs deleted file mode 100644 index c926ba5c09..0000000000 --- a/aws/sdk/integration-tests/s3/tests/is-truncated-pagination.rs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#![cfg(feature = "test-util")] - -use aws_credential_types::provider::SharedCredentialsProvider; -use aws_sdk_s3::Config; -use aws_sdk_s3::{config::Credentials, config::Region, Client}; -use aws_smithy_runtime::client::http::test_util::{ReplayEvent, StaticReplayClient}; -use aws_smithy_types::body::SdkBody; - -fn mk_response(part_marker: u8) -> http::Response { - let (part_num_marker, next_num_marker, is_truncated) = if part_marker < 3 { - (part_marker, part_marker + 1, true) - } else { - (part_marker, 0, false) - }; - let body = format!( - "\n - - foo-bar-bucket - test.txt - N0taR34lUpl0adId - {part_num_marker} - {next_num_marker} - 1 - {is_truncated} - - 4 - 2024-06-03T16:01:05.000Z - "1234" - 5242880 - - " - ); - http::Response::builder().body(SdkBody::from(body)).unwrap() -} - -fn mk_request() -> http::Request { - http::Request::builder() - .uri("https://some-test-bucket.s3.us-east-1.amazonaws.com/test.txt?part-number-marker=PartNumberMarker&uploadId=UploadId") - .body(SdkBody::empty()) - .unwrap() -} - -#[tokio::test] -async fn is_truncated_pagination_does_not_loop() { - let http_client = StaticReplayClient::new(vec![ - ReplayEvent::new(mk_request(), mk_response(0)), - ReplayEvent::new(mk_request(), mk_response(1)), - ReplayEvent::new(mk_request(), mk_response(2)), - ReplayEvent::new(mk_request(), mk_response(3)), - //The events below should never be called because the pagination should - //terminate with the event above - ReplayEvent::new(mk_request(), mk_response(0)), - ReplayEvent::new(mk_request(), mk_response(1)), - ]); - - let config = Config::builder() - .credentials_provider(SharedCredentialsProvider::new( - Credentials::for_tests_with_session_token(), - )) - .region(Region::new("us-east-1")) - .http_client(http_client.clone()) - .with_test_defaults() - .build(); - let client = Client::from_conf(config); - - let list_parts_res = client - .list_parts() - .bucket("some-test-bucket") - .key("test.txt") - .upload_id("N0taR34lUpl0adId") - .max_parts(1) - .into_paginator() - .send() - .collect::>() - .await; - - // Confirm that the pagination stopped calling the http client after the - // first page with is_truncated = false - assert_eq!(list_parts_res.len(), 4) -} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt index 8c8a4784e6..e81a94247e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt @@ -6,11 +6,16 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.traits.IsTruncatedPaginatorTrait import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest +import software.amazon.smithy.rust.codegen.core.util.letIf internal class PaginatorGeneratorTest { private val model = @@ -57,7 +62,8 @@ internal class PaginatorGeneratorTest { } structure GetFoosOutput { - inner: Inner + inner: Inner, + isTruncated: Boolean, } list StringList { @@ -79,4 +85,22 @@ internal class PaginatorGeneratorTest { } } } + + @Test + fun `isTruncated paginators compile`() { + // Adding IsTruncated trait to the output shape + val modifiedModel = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(shape.isStructureShape && shape.toShapeId() == ShapeId.from("test#GetFoosOutput")) { + (it as StructureShape).toBuilder().addTrait(IsTruncatedPaginatorTrait()).build() + } + } + + clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + rustCrate.integrationTest("is_truncated_paginators_generated") { + Attribute.AllowUnusedImports.render(this) + rust("use ${clientCodegenContext.moduleUseName()}::operation::paginated_list::paginator::PaginatedListPaginator;") + } + } + } }