AWS SDK

AWS SDK

rev. 9737746e67c6e91008f0c6cd861edacaf8d4a79a..042b27f84b68de1497919ecd1f99141980a9a503 (ignoring whitespace)

Files changed:

tmp-codegen-diff/services/build.gradle.kts

@@ -1,1 +36,36 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
import aws.sdk.kotlin.gradle.dsl.configurePublishing
    6         -
import aws.sdk.kotlin.gradle.kmp.*
           6  +
import aws.sdk.kotlin.gradle.kmp.kotlin
    7      7   
import aws.sdk.kotlin.gradle.util.typedProp
    8      8   
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
    9      9   
import java.time.LocalDateTime
   10     10   
   11     11   
plugins {
   12     12   
    `maven-publish`
   13     13   
    `dokka-convention`
   14     14   
    alias(libs.plugins.aws.kotlin.repo.tools.kmp) apply false
   15     15   
}
   16     16   
@@ -82,82 +164,167 @@
  102    102   
                        }
  103    103   
  104    104   
                        // Run the tests with the classpath containing the compile dependencies (including 'main'),
  105    105   
                        // runtime dependencies, and the outputs of this compilation:
  106    106   
                        classpath = compileDependencyFiles + runtimeDependencyFiles + output.allOutputs
  107    107   
  108    108   
                        // Run only the tests from this compilation's outputs:
  109    109   
                        testClassesDirs = output.classesDirs
  110    110   
  111    111   
                        useJUnitPlatform()
  112         -
                        testLogging {
  113         -
                            events("passed", "skipped", "failed")
  114         -
                            showStandardStreams = true
  115         -
                            showStackTraces = true
  116         -
                            showExceptions = true
  117         -
                            exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
  118         -
                        }
  119    112   
  120    113   
                        // model a random input to enable re-running e2e tests back to back without
  121    114   
                        // up-to-date checks or cache getting in the way
  122    115   
                        inputs.property("integration.datetime", LocalDateTime.now())
  123    116   
                        systemProperty("org.slf4j.simpleLogger.defaultLogLevel", System.getProperty("org.slf4j.simpleLogger.defaultLogLevel", "WARN"))
  124    117   
                    }
  125    118   
                }
  126    119   
            }
  127    120   
        }
  128    121   
    }
  129    122   
  130    123   
    tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
  131    124   
        compilerOptions {
  132    125   
            allWarningsAsErrors.set(false) // FIXME Tons of errors occur in generated code
  133    126   
            jvmTarget.set(JvmTarget.JVM_1_8) // fixes outgoing variant metadata: https://github.com/smithy-lang/smithy-kotlin/issues/258
  134    127   
            freeCompilerArgs.add("-Xjdk-release=1.8")
  135    128   
        }
  136    129   
    }
  137    130   
         131  +
    tasks.withType<org.gradle.api.tasks.testing.AbstractTestTask>().configureEach {
         132  +
        testLogging {
         133  +
            events("passed", "skipped", "failed")
         134  +
            showStandardStreams = true
         135  +
            showStackTraces = true
         136  +
            showExceptions = true
         137  +
            exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
         138  +
        }
         139  +
    }
         140  +
  138    141   
    configurePublishing("aws-sdk-kotlin")
  139    142   
    publishing {
  140    143   
        publications.all {
  141    144   
            if (this !is MavenPublication) return@all
  142    145   
            project.afterEvaluate {
  143    146   
                val sdkId = project.typedProp<String>("aws.sdk.id") ?: error("service build `${project.name}` is missing `aws.sdk.id` property required for publishing")
  144    147   
                pom.properties.put("aws.sdk.id", sdkId)
  145    148   
            }
  146    149   
        }
  147    150   
    }

tmp-codegen-diff/services/s3/common/test/aws/sdk/kotlin/services/s3/CreateClientTest.kt

@@ -1,1 +33,33 @@
   11     11   
   12     12   
/**
   13     13   
 * Validate the way service clients can be constructed.
   14     14   
 *
   15     15   
 * These are written against S3 but apply generically to any client.
   16     16   
 */
   17     17   
class CreateClientTest {
   18     18   
    @Test
   19     19   
    fun testMissingRegion() {
   20     20   
        // Should _not_ throw an exception since region is optional
   21         -
        S3Client { }
          21  +
        S3Client { }.use { }
   22     22   
    }
   23     23   
   24     24   
    @Test
   25     25   
    fun testDefaults() {
   26     26   
        S3Client { region = "us-east-2" }.use { }
   27     27   
    }
   28     28   
   29     29   
    @Test
   30     30   
    fun testFromEnvironmentWithOverrides() = runTest {
   31     31   
        S3Client.fromEnvironment { region = "us-east-2" }.use { }

tmp-codegen-diff/services/s3/common/test/aws/sdk/kotlin/services/s3/express/DefaultS3ExpressCredentialsProviderTest.kt

@@ -1,1 +285,294 @@
    9      9   
import aws.sdk.kotlin.services.s3.S3Client
   10     10   
import aws.sdk.kotlin.services.s3.model.CreateSessionRequest
   11     11   
import aws.sdk.kotlin.services.s3.model.CreateSessionResponse
   12     12   
import aws.sdk.kotlin.services.s3.model.SessionCredentials
   13     13   
import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
   14     14   
import aws.smithy.kotlin.runtime.io.use
   15     15   
import aws.smithy.kotlin.runtime.operation.ExecutionContext
   16     16   
import aws.smithy.kotlin.runtime.time.ManualClock
   17     17   
import kotlinx.coroutines.*
   18     18   
import kotlinx.coroutines.test.runTest
   19         -
import kotlin.test.*
          19  +
import kotlin.test.Test
          20  +
import kotlin.test.assertEquals
          21  +
import kotlin.test.assertFalse
   20     22   
import kotlin.time.ComparableTimeMark
   21     23   
import kotlin.time.Duration.Companion.milliseconds
   22     24   
import kotlin.time.Duration.Companion.minutes
   23     25   
import kotlin.time.Duration.Companion.seconds
   24     26   
import kotlin.time.TestTimeSource
   25     27   
   26     28   
private val DEFAULT_BASE_CREDENTIALS = Credentials("accessKeyId", "secretAccessKey", "sessionToken")
   27     29   
   28     30   
class DefaultS3ExpressCredentialsProviderTest {
   29     31   
    @Test
   30     32   
    fun testCreateSessionCredentials() = runTest {
   31     33   
        val timeSource = TestTimeSource()
   32     34   
        val clock = ManualClock()
   33     35   
   34     36   
        val expectedCredentials = SessionCredentials {
   35     37   
            accessKeyId = "access"
   36     38   
            secretAccessKey = "secret"
   37     39   
            sessionToken = "session"
   38     40   
            expiration = clock.now() + 5.minutes
   39     41   
        }
   40     42   
   41         -
        val client = TestS3Client(expectedCredentials)
   42         -
          43  +
        TestS3Client(expectedCredentials).use { client ->
   43     44   
            DefaultS3ExpressCredentialsProvider(timeSource, clock).use { provider ->
   44     45   
                val credentials = provider.createSessionCredentials(
   45     46   
                    S3ExpressCredentialsCacheKey("bucket", DEFAULT_BASE_CREDENTIALS),
   46     47   
                    client,
   47     48   
                )
   48     49   
                assertFalse(credentials.isExpired)
   49     50   
                assertEquals(timeSource.markNow() + 5.minutes, credentials.expiresAt)
   50     51   
            }
   51     52   
        }
          53  +
    }
   52     54   
   53     55   
    @Test
   54     56   
    fun testSyncRefresh() = runTest {
   55     57   
        val timeSource = TestTimeSource()
   56     58   
        val clock = ManualClock()
   57     59   
   58     60   
        // Entry expired 30 seconds ago, next `resolve` call should trigger a sync refresh
   59     61   
        val cache = S3ExpressCredentialsCache()
   60     62   
        val entry = getCacheEntry(timeSource.markNow() - 30.seconds)
   61     63   
        cache.put(entry.key, entry.value)
   62     64   
   63     65   
        val expectedCredentials = SessionCredentials {
   64     66   
            accessKeyId = "access"
   65     67   
            secretAccessKey = "secret"
   66     68   
            sessionToken = "session"
   67     69   
            expiration = clock.now() + 5.minutes
   68     70   
        }
   69     71   
   70         -
        val testClient = TestS3Client(expectedCredentials)
          72  +
        TestS3Client(expectedCredentials).use { testClient ->
   71     73   
            DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes).use { provider ->
   72     74   
                val attributes = ExecutionContext.build {
   73     75   
                    this.attributes[S3Attributes.ExpressClient] = testClient
   74     76   
                    this.attributes[S3Attributes.Bucket] = "bucket"
   75     77   
                }
   76     78   
   77     79   
                provider.resolve(attributes)
   78     80   
            }
   79     81   
            assertEquals(1, testClient.numCreateSession)
   80     82   
        }
          83  +
    }
   81     84   
   82     85   
    @Test
   83     86   
    fun testAsyncRefresh() = runTest {
   84     87   
        val timeSource = TestTimeSource()
   85     88   
        val clock = ManualClock()
   86     89   
   87     90   
        // Entry expires in 30 seconds, refresh buffer is 1 minute. Next `resolve` call should trigger the async refresh
   88     91   
        val cache = S3ExpressCredentialsCache()
   89     92   
        val entry = getCacheEntry(timeSource.markNow() + 30.seconds)
   90     93   
        cache.put(entry.key, entry.value)
   91     94   
   92     95   
        val expectedCredentials = SessionCredentials {
   93     96   
            accessKeyId = "access"
   94     97   
            secretAccessKey = "secret"
   95     98   
            sessionToken = "session"
   96     99   
            expiration = clock.now() + 5.minutes
   97    100   
        }
   98    101   
   99         -
        val testClient = TestS3Client(expectedCredentials)
  100         -
         102  +
        TestS3Client(expectedCredentials).use { testClient ->
  101    103   
            val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes)
  102    104   
  103    105   
            val attributes = ExecutionContext.build {
  104    106   
                this.attributes[S3Attributes.ExpressClient] = testClient
  105    107   
                this.attributes[S3Attributes.Bucket] = "bucket"
  106    108   
            }
  107    109   
            provider.resolve(attributes)
  108    110   
  109    111   
            // allow the async refresh to initiate before closing the provider
  110    112   
            runBlocking { delay(500.milliseconds) }
  111    113   
  112    114   
            provider.close()
  113    115   
            provider.coroutineContext.job.join()
  114    116   
  115    117   
            assertEquals(1, testClient.numCreateSession)
  116    118   
        }
         119  +
    }
  117    120   
  118    121   
    @Test
  119    122   
    fun testAsyncRefreshDebounce() = runTest {
  120    123   
        val timeSource = TestTimeSource()
  121    124   
        val clock = ManualClock()
  122    125   
  123    126   
        // Entry expires in 30 seconds, refresh buffer is 1 minute. Next `resolve` call should trigger the async refresh
  124    127   
        val cache = S3ExpressCredentialsCache()
  125    128   
        val entry = getCacheEntry(expiration = timeSource.markNow() + 30.seconds)
  126    129   
        cache.put(entry.key, entry.value)
  127    130   
  128    131   
        val expectedCredentials = SessionCredentials {
  129    132   
            accessKeyId = "access"
  130    133   
            secretAccessKey = "secret"
  131    134   
            sessionToken = "session"
  132    135   
            expiration = clock.now() + 5.minutes
  133    136   
        }
  134    137   
  135         -
        val testClient = TestS3Client(expectedCredentials)
  136         -
         138  +
        TestS3Client(expectedCredentials).use { testClient ->
  137    139   
            val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes)
  138    140   
  139    141   
            val attributes = ExecutionContext.build {
  140    142   
                this.attributes[S3Attributes.ExpressClient] = testClient
  141    143   
                this.attributes[S3Attributes.Bucket] = "bucket"
  142    144   
            }
  143    145   
            val calls = (1..5).map {
  144    146   
                async { provider.resolve(attributes) }
  145    147   
            }
  146    148   
            calls.awaitAll()
  147    149   
  148    150   
            // allow the async refresh to initiate before closing the provider
  149    151   
            runBlocking { delay(500.milliseconds) }
  150    152   
  151    153   
            provider.close()
  152    154   
            provider.coroutineContext.job.join()
  153    155   
  154    156   
            assertEquals(1, testClient.numCreateSession)
  155    157   
        }
         158  +
    }
  156    159   
  157    160   
    @Test
  158    161   
    fun testAsyncRefreshHandlesFailures() = runTest {
  159    162   
        val timeSource = TestTimeSource()
  160    163   
        val clock = ManualClock()
  161    164   
  162    165   
        // Entry expires in 30 seconds, refresh buffer is 1 minute. Next `resolve` call should trigger the async refresh
  163    166   
        val cache = S3ExpressCredentialsCache()
  164    167   
        val successEntry = getCacheEntry(timeSource.markNow() + 30.seconds, bucket = "SuccessfulBucket")
  165    168   
        val failedEntry = getCacheEntry(timeSource.markNow() + 30.seconds, bucket = "ExceptionBucket")
  166    169   
        cache.put(successEntry.key, successEntry.value)
  167    170   
        cache.put(failedEntry.key, failedEntry.value)
  168    171   
  169    172   
        val expectedCredentials = SessionCredentials {
  170    173   
            accessKeyId = "access"
  171    174   
            secretAccessKey = "secret"
  172    175   
            sessionToken = "session"
  173    176   
            expiration = clock.now() + 5.minutes
  174    177   
        }
  175    178   
  176    179   
        // client should throw an exception when `ExceptionBucket` credentials are fetched, but it should be caught
  177         -
        val testClient = TestS3Client(expectedCredentials, throwExceptionOnBucketNamed = "ExceptionBucket")
  178         -
         180  +
        TestS3Client(expectedCredentials, throwExceptionOnBucketNamed = "ExceptionBucket").use { testClient ->
  179    181   
            val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes)
  180    182   
            val attributes = ExecutionContext.build {
  181    183   
                this.attributes[S3Attributes.ExpressClient] = testClient
  182    184   
                this.attributes[S3Attributes.Bucket] = "ExceptionBucket"
  183    185   
            }
  184    186   
            provider.resolve(attributes)
  185    187   
  186    188   
            withTimeout(5.seconds) {
  187    189   
                while (testClient.numCreateSession != 1) {
  188    190   
                    yield()
  189    191   
                }
  190    192   
            }
  191    193   
            assertEquals(1, testClient.numCreateSession)
  192    194   
  193    195   
            attributes[S3Attributes.Bucket] = "SuccessfulBucket"
  194    196   
            provider.resolve(attributes)
  195    197   
  196    198   
            withTimeout(5.seconds) {
  197    199   
                while (testClient.numCreateSession != 2) {
  198    200   
                    yield()
  199    201   
                }
  200    202   
            }
  201    203   
  202    204   
            provider.close()
  203    205   
            provider.coroutineContext.job.join()
  204    206   
  205    207   
            assertEquals(2, testClient.numCreateSession)
  206    208   
        }
         209  +
    }
  207    210   
  208    211   
    @Test
  209    212   
    fun testAsyncRefreshClosesImmediately() = runTest {
  210    213   
        val timeSource = TestTimeSource()
  211    214   
        val clock = ManualClock()
  212    215   
  213    216   
        // Entry expires in 30 seconds, refresh buffer is 1 minute. Next `resolve` call should trigger the async refresh
  214    217   
        val cache = S3ExpressCredentialsCache()
  215    218   
        val entry = getCacheEntry(timeSource.markNow() + 30.seconds)
  216    219   
        cache.put(entry.key, entry.value)
  217    220   
  218    221   
        val expectedCredentials = SessionCredentials {
  219    222   
            accessKeyId = "access"
  220    223   
            secretAccessKey = "secret"
  221    224   
            sessionToken = "session"
  222    225   
            expiration = clock.now() + 5.minutes
  223    226   
        }
  224    227   
  225    228   
        val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes)
  226    229   
  227         -
        val blockingTestS3Client = object : TestS3Client(expectedCredentials) {
         230  +
        class BlockingTestS3Client : TestS3Client(expectedCredentials) {
  228    231   
            override suspend fun createSession(input: CreateSessionRequest): CreateSessionResponse {
  229    232   
                delay(10.seconds)
  230    233   
                numCreateSession += 1
  231    234   
                return CreateSessionResponse { credentials = expectedCredentials }
  232    235   
            }
  233    236   
        }
  234    237   
         238  +
        BlockingTestS3Client().use { blockingTestS3Client ->
  235    239   
            val attributes = ExecutionContext.build {
  236    240   
                this.attributes[S3Attributes.ExpressClient] = blockingTestS3Client
  237    241   
                this.attributes[S3Attributes.Bucket] = "bucket"
  238    242   
            }
  239    243   
  240    244   
            withTimeout(5.seconds) {
  241    245   
                provider.resolve(attributes)
  242    246   
                provider.close()
  243    247   
            }
  244    248   
            assertEquals(0, blockingTestS3Client.numCreateSession)
  245    249   
        }
         250  +
    }
  246    251   
  247    252   
    /**
  248    253   
     * Get an instance of [Map.Entry<S3ExpressCredentialsCacheKey, S3ExpressCredentialsCacheValue>] using the given [expiration],
  249    254   
     * [bucket], and optional [bootstrapCredentials] and [sessionCredentials].
  250    255   
     */
  251    256   
    private fun getCacheEntry(
  252    257   
        expiration: ComparableTimeMark,
  253    258   
        bucket: String = "bucket",
  254    259   
        bootstrapCredentials: Credentials = Credentials(accessKeyId = "accessKeyId", secretAccessKey = "secretAccessKey", sessionToken = "sessionToken"),
  255    260   
        sessionCredentials: Credentials = Credentials(accessKeyId = "s3AccessKeyId", secretAccessKey = "s3SecretAccessKey", sessionToken = "s3SessionToken"),
  256    261   
    ): S3ExpressCredentialsCacheEntry = mapOf(
  257    262   
        S3ExpressCredentialsCacheKey(bucket, bootstrapCredentials) to S3ExpressCredentialsCacheValue(ExpiringValue(sessionCredentials, expiration)),
  258    263   
    ).entries.first()
  259    264   
  260    265   
    /**
  261    266   
     * A test S3Client used to mock calls to s3:CreateSession.
  262    267   
     * @param expectedCredentials the expected session credentials returned from s3:CreateSession
  263    268   
     * @param client the base S3 client used to implement other operations, though they are unused.
  264    269   
     * @param throwExceptionOnBucketNamed an optional bucket name, which when specified and present in the [CreateSessionRequest], will
  265    270   
     * cause the client to throw an exception instead of returning credentials. Used for testing s3:CreateSession failures.
  266    271   
     */
  267    272   
    private open inner class TestS3Client(
  268    273   
        val expectedCredentials: SessionCredentials,
  269    274   
        val baseCredentials: Credentials = DEFAULT_BASE_CREDENTIALS,
  270    275   
        val client: S3Client = S3Client { credentialsProvider = StaticCredentialsProvider(baseCredentials) },
  271    276   
        val throwExceptionOnBucketNamed: String? = null,
  272    277   
    ) : S3Client by client {
  273    278   
        var numCreateSession = 0
  274    279   
  275    280   
        override suspend fun createSession(input: CreateSessionRequest): CreateSessionResponse {
  276    281   
            numCreateSession += 1
  277    282   
            throwExceptionOnBucketNamed?.let {
  278    283   
                if (input.bucket == it) {
  279    284   
                    throw Exception("Failed to create session credentials for bucket: $throwExceptionOnBucketNamed")
  280    285   
                }
  281    286   
            }
  282    287   
            return CreateSessionResponse { credentials = expectedCredentials }
  283    288   
        }
         289  +
         290  +
        override fun close() {
         291  +
            client.close()
         292  +
        }
  284    293   
    }
  285    294   
}

tmp-codegen-diff/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/ExpiresFieldInterceptorTest.kt

@@ -1,1 +75,78 @@
    6      6   
    7      7   
import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider
    8      8   
import aws.sdk.kotlin.services.s3.S3Client
    9      9   
import aws.sdk.kotlin.services.s3.model.GetObjectRequest
   10     10   
import aws.smithy.kotlin.runtime.http.Headers
   11     11   
import aws.smithy.kotlin.runtime.http.HeadersBuilder
   12     12   
import aws.smithy.kotlin.runtime.http.HttpBody
   13     13   
import aws.smithy.kotlin.runtime.http.HttpStatusCode
   14     14   
import aws.smithy.kotlin.runtime.http.response.HttpResponse
   15     15   
import aws.smithy.kotlin.runtime.httptest.buildTestConnection
          16  +
import aws.smithy.kotlin.runtime.io.use
   16     17   
import aws.smithy.kotlin.runtime.time.Instant
   17     18   
import kotlinx.coroutines.test.runTest
   18     19   
import kotlin.test.Test
   19     20   
import kotlin.test.assertEquals
   20     21   
import kotlin.test.assertNull
   21     22   
   22     23   
class ExpiresFieldInterceptorTest {
   23     24   
    private fun newTestClient(
   24     25   
        status: HttpStatusCode = HttpStatusCode.OK,
   25     26   
        headers: Headers = Headers.Empty,
   26     27   
    ): S3Client =
   27     28   
        S3Client {
   28     29   
            region = "us-east-1"
   29     30   
            credentialsProvider = StaticCredentialsProvider {
   30     31   
                accessKeyId = "accessKeyId"
   31     32   
                secretAccessKey = "secretAccessKey"
   32     33   
            }
   33     34   
            httpClient = buildTestConnection {
   34     35   
                expect(HttpResponse(status, headers, body = HttpBody.Empty))
   35     36   
            }
   36     37   
        }
   37     38   
   38     39   
    @Test
   39     40   
    fun testHandlesParsableExpiresField() = runTest {
   40     41   
        val expectedHeaders = HeadersBuilder().apply {
   41     42   
            append("Expires", "Mon, 1 Apr 2024 00:00:00 +0000")
   42     43   
        }.build()
   43     44   
   44         -
        val s3 = newTestClient(headers = expectedHeaders)
          45  +
        newTestClient(headers = expectedHeaders).use { s3 ->
   45     46   
            s3.getObject(
   46     47   
                GetObjectRequest {
   47     48   
                    bucket = "test"
   48     49   
                    key = "key"
   49     50   
                },
   50     51   
            ) {
   51     52   
                assertEquals(Instant.fromEpochSeconds(1711929600), it.expires)
   52     53   
                assertEquals("Mon, 1 Apr 2024 00:00:00 +0000", it.expiresString)
   53     54   
            }
   54     55   
        }
          56  +
    }
   55     57   
   56     58   
    @Test
   57     59   
    fun testHandlesUnparsableExpiresField() = runTest {
   58     60   
        val invalidExpiresField = "Tomorrow or maybe the day after?"
   59     61   
   60     62   
        val expectedHeaders = HeadersBuilder().apply {
   61     63   
            append("Expires", invalidExpiresField)
   62     64   
        }.build()
   63     65   
   64         -
        val s3 = newTestClient(headers = expectedHeaders)
          66  +
        newTestClient(headers = expectedHeaders).use { s3 ->
   65     67   
            s3.getObject(
   66     68   
                GetObjectRequest {
   67     69   
                    bucket = "test"
   68     70   
                    key = "key"
   69     71   
                },
   70     72   
            ) {
   71     73   
                assertNull(it.expires)
   72     74   
                assertEquals(invalidExpiresField, it.expiresString)
   73     75   
            }
   74     76   
        }
          77  +
    }
   75     78   
}

tmp-codegen-diff/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/Handle200ErrorsInterceptorTest.kt

@@ -1,1 +122,126 @@
    8      8   
import aws.sdk.kotlin.services.s3.deleteObject
    9      9   
import aws.sdk.kotlin.services.s3.deleteObjects
   10     10   
import aws.sdk.kotlin.services.s3.model.S3Exception
   11     11   
import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
   12     12   
import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider
   13     13   
import aws.smithy.kotlin.runtime.collections.Attributes
   14     14   
import aws.smithy.kotlin.runtime.http.HttpBody
   15     15   
import aws.smithy.kotlin.runtime.http.HttpStatusCode
   16     16   
import aws.smithy.kotlin.runtime.http.response.HttpResponse
   17     17   
import aws.smithy.kotlin.runtime.httptest.buildTestConnection
          18  +
import aws.smithy.kotlin.runtime.io.use
   18     19   
import kotlinx.coroutines.test.runTest
   19     20   
import kotlin.test.Test
   20     21   
import kotlin.test.assertEquals
   21     22   
import kotlin.test.assertFailsWith
   22     23   
   23     24   
private const val REQUEST_ID = "K2H6N7ZGQT6WHCEG"
   24     25   
private const val EXT_REQUEST_ID = "WWoZlnK4pTjKCYn6eNV7GgOurabfqLkjbSyqTvDMGBaI9uwzyNhSaDhOCPs8paFGye7S6b/AB3A="
   25     26   
   26     27   
class Handle200ErrorsInterceptorTest {
   27     28   
   28     29   
    object TestCredentialsProvider : CredentialsProvider {
   29     30   
        override suspend fun resolve(attributes: Attributes): Credentials = Credentials("AKID", "SECRET")
   30     31   
    }
   31     32   
    private val errorResponsePayload = """
   32     33   
            <Error>
   33     34   
                <Code>SlowDown</Code>
   34     35   
                <Message>Please reduce your request rate.</Message>
   35     36   
                <RequestId>$REQUEST_ID</RequestId>
   36     37   
                <HostId>$EXT_REQUEST_ID</HostId>
   37     38   
            </Error>
   38     39   
    """.trimIndent().encodeToByteArray()
   39     40   
   40     41   
    private fun newTestClient(
   41     42   
        status: HttpStatusCode = HttpStatusCode.OK,
   42     43   
        payload: ByteArray = errorResponsePayload,
   43     44   
    ): S3Client =
   44     45   
        S3Client {
   45     46   
            region = "us-east-1"
   46     47   
            credentialsProvider = TestCredentialsProvider
   47     48   
            retryStrategy {
   48     49   
                maxAttempts = 1
   49     50   
            }
   50     51   
            httpClient = buildTestConnection {
   51     52   
                expect(HttpResponse(status, body = HttpBody.fromBytes(payload)))
   52     53   
            }
   53     54   
        }
   54     55   
   55     56   
    fun assertException(ex: S3Exception) {
   56     57   
        val expectedMessage = "Please reduce your request rate."
   57     58   
        assertEquals("SlowDown", ex.sdkErrorMetadata.errorCode)
   58     59   
        assertEquals(expectedMessage, ex.sdkErrorMetadata.errorMessage)
   59     60   
        assertEquals("$expectedMessage, Request ID: $REQUEST_ID, Extended request ID: $EXT_REQUEST_ID", ex.message)
   60     61   
        assertEquals(REQUEST_ID, ex.sdkErrorMetadata.requestId)
   61     62   
        assertEquals(EXT_REQUEST_ID, ex.sdkErrorMetadata.requestId2)
   62     63   
    }
   63     64   
   64     65   
    @Test
   65     66   
    fun testHandle200ErrorsWithNoExpectedBody() = runTest {
   66         -
        val s3 = newTestClient()
          67  +
        newTestClient().use { s3 ->
   67     68   
            val ex = assertFailsWith<S3Exception> {
   68     69   
                s3.deleteObject {
   69     70   
                    bucket = "test"
   70     71   
                    key = "key"
   71     72   
                }
   72     73   
            }
   73     74   
            assertException(ex)
   74     75   
        }
          76  +
    }
   75     77   
   76     78   
    @Test
   77     79   
    fun testHandle200ErrorsWithExpectedBody() = runTest {
   78         -
        val s3 = newTestClient()
          80  +
        newTestClient().use { s3 ->
   79     81   
            val ex = assertFailsWith<S3Exception> {
   80     82   
                s3.deleteObjects { bucket = "test" }
   81     83   
            }
   82     84   
            assertException(ex)
   83     85   
        }
          86  +
    }
   84     87   
   85     88   
    @Test
   86     89   
    fun testNonErrorPayload() = runTest {
   87     90   
        val payload = """
   88     91   
           <?xml version="1.0" encoding="UTF-8"?>
   89     92   
           <DeleteResult>
   90     93   
              <Deleted>
   91     94   
                 <Key>my-key</Key>
   92     95   
              </Deleted>
   93     96   
           </DeleteResult>
   94     97   
        """.trimIndent().encodeToByteArray()
   95     98   
        val s3 = newTestClient(payload = payload)
   96     99   
        val response = s3.deleteObjects { bucket = "test" }
   97    100   
        assertEquals("my-key", response.deleted?.first()?.key)
   98    101   
    }
   99    102   
  100    103   
    @Test
  101    104   
    fun testErrorPayloadUnmodified() = runTest {
  102    105   
        val payload = """
  103    106   
           <?xml version="1.0" encoding="UTF-8"?>
  104    107   
            <Error>
  105    108   
                <Code>FooError</Code>
  106    109   
                <Message>Please use less foos.</Message>
  107    110   
                <RequestId>rid</RequestId>
  108    111   
                <HostId>rid2</HostId>
  109    112   
            </Error>
  110    113   
        """.trimIndent().encodeToByteArray()
  111         -
        val s3 = newTestClient(HttpStatusCode.BadRequest, payload)
         114  +
        newTestClient(HttpStatusCode.BadRequest, payload).use { s3 ->
  112    115   
            val ex = assertFailsWith<S3Exception> {
  113    116   
                s3.deleteObjects { bucket = "test" }
  114    117   
            }
  115    118   
            val expectedMessage = "Please use less foos."
  116    119   
            assertEquals("$expectedMessage, Request ID: rid, Extended request ID: rid2", ex.message)
  117    120   
            assertEquals(expectedMessage, ex.sdkErrorMetadata.errorMessage)
  118    121   
            assertEquals("FooError", ex.sdkErrorMetadata.errorCode)
  119    122   
            assertEquals("rid", ex.sdkErrorMetadata.requestId)
  120    123   
            assertEquals("rid2", ex.sdkErrorMetadata.requestId2)
  121    124   
        }
         125  +
    }
  122    126   
}

tmp-codegen-diff/services/s3/e2eTest/src/PaginatorTest.kt

@@ -11,11 +70,71 @@
   31     31   
    private lateinit var testBucket: String
   32     32   
   33     33   
    @BeforeAll
   34     34   
    fun createResources(): Unit = runBlocking {
   35     35   
        testBucket = S3TestUtils.getTestBucket(client)
   36     36   
    }
   37     37   
   38     38   
    @AfterAll
   39     39   
    fun cleanup() = runBlocking {
   40     40   
        S3TestUtils.deleteBucketAndAllContents(client, testBucket)
          41  +
        client.close()
   41     42   
    }
   42     43   
   43     44   
    // ListParts has a strange pagination termination condition via [IsTerminated]. Verify it actually works correctly.
   44     45   
    @Test
   45     46   
    fun testListPartsPagination() = runBlocking {
   46     47   
        val chunk = "!".repeat(5 * 1024 * 1024).encodeToByteArray() // Parts must be at least 5MB
   47     48   
        val expectedParts = (1..10).toList()
   48     49   
   49     50   
        val id = client.createMultipartUpload {
   50     51   
            bucket = testBucket

tmp-codegen-diff/services/s3/e2eTest/src/S3IntegrationTest.kt

@@ -1,1 +83,82 @@
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
package aws.sdk.kotlin.e2etest
    6      6   
    7      7   
import aws.sdk.kotlin.services.s3.*
    8      8   
import aws.sdk.kotlin.services.s3.model.*
    9      9   
import aws.sdk.kotlin.testing.PRINTABLE_CHARS
   10     10   
import aws.sdk.kotlin.testing.withAllEngines
   11     11   
import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext
   12         -
import aws.smithy.kotlin.runtime.content.ByteStream
   13         -
import aws.smithy.kotlin.runtime.content.asByteStream
   14         -
import aws.smithy.kotlin.runtime.content.decodeToString
   15         -
import aws.smithy.kotlin.runtime.content.fromFile
   16         -
import aws.smithy.kotlin.runtime.content.toByteArray
   17         -
import aws.smithy.kotlin.runtime.content.toByteStream
          12  +
import aws.smithy.kotlin.runtime.content.*
   18     13   
import aws.smithy.kotlin.runtime.hashing.sha256
   19     14   
import aws.smithy.kotlin.runtime.http.HttpException
   20     15   
import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor
   21     16   
import aws.smithy.kotlin.runtime.http.request.HttpRequest
   22     17   
import aws.smithy.kotlin.runtime.testing.RandomTempFile
   23     18   
import aws.smithy.kotlin.runtime.text.encoding.encodeToHex
   24         -
import kotlinx.coroutines.*
          19  +
import kotlinx.coroutines.async
          20  +
import kotlinx.coroutines.awaitAll
   25     21   
import kotlinx.coroutines.flow.flow
   26     22   
import kotlinx.coroutines.flow.toList
          23  +
import kotlinx.coroutines.runBlocking
          24  +
import kotlinx.coroutines.withTimeout
   27     25   
import org.junit.jupiter.api.AfterAll
   28     26   
import org.junit.jupiter.api.BeforeAll
   29     27   
import org.junit.jupiter.api.TestInstance
   30     28   
import java.io.File
   31         -
import java.util.UUID
          29  +
import java.util.*
   32     30   
import kotlin.test.*
   33     31   
import kotlin.time.Duration.Companion.seconds
   34     32   
   35     33   
/**
   36     34   
 * Tests for bucket operations and presigner
   37     35   
 */
   38     36   
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
   39     37   
class S3BucketOpsIntegrationTest {
   40     38   
    private val client = S3Client {
   41     39   
        region = S3TestUtils.DEFAULT_REGION
   42     40   
    }
   43     41   
   44     42   
    private lateinit var testBucket: String
   45     43   
   46     44   
    @BeforeAll
   47     45   
    fun createResources(): Unit = runBlocking {
   48     46   
        testBucket = S3TestUtils.getTestBucket(client)
   49     47   
    }
   50     48   
   51     49   
    @AfterAll
   52     50   
    fun cleanup() = runBlocking {
   53     51   
        S3TestUtils.deleteBucketAndAllContents(client, testBucket)
          52  +
        client.close()
   54     53   
    }
   55     54   
   56     55   
    @Test
   57     56   
    fun testPutObjectFromMemory(): Unit = runBlocking {
   58     57   
        val contents = """
   59     58   
            A lep is a ball.
   60     59   
            A tay is a hammer.
   61     60   
            A korf is a tiger.
   62     61   
            A flix is a comb.
   63     62   
            A wogsin is a gift.