diff --git a/services/build.gradle.kts b/services/build.gradle.kts index d2d5f4189ea..2f39727e29b 100644 --- a/services/build.gradle.kts +++ b/services/build.gradle.kts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import aws.sdk.kotlin.gradle.dsl.configurePublishing -import aws.sdk.kotlin.gradle.kmp.* +import aws.sdk.kotlin.gradle.kmp.kotlin import aws.sdk.kotlin.gradle.util.typedProp import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.time.LocalDateTime @@ -109,13 +109,6 @@ subprojects { testClassesDirs = output.classesDirs useJUnitPlatform() - testLogging { - events("passed", "skipped", "failed") - showStandardStreams = true - showStackTraces = true - showExceptions = true - exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - } // model a random input to enable re-running e2e tests back to back without // up-to-date checks or cache getting in the way @@ -135,6 +128,16 @@ subprojects { } } + tasks.withType().configureEach { + testLogging { + events("passed", "skipped", "failed") + showStandardStreams = true + showStackTraces = true + showExceptions = true + exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL + } + } + configurePublishing("aws-sdk-kotlin") publishing { publications.all { diff --git a/services/s3/common/test/aws/sdk/kotlin/services/s3/CreateClientTest.kt b/services/s3/common/test/aws/sdk/kotlin/services/s3/CreateClientTest.kt index 224a79de54c..d25af4a4417 100644 --- a/services/s3/common/test/aws/sdk/kotlin/services/s3/CreateClientTest.kt +++ b/services/s3/common/test/aws/sdk/kotlin/services/s3/CreateClientTest.kt @@ -18,7 +18,7 @@ class CreateClientTest { @Test fun testMissingRegion() { // Should _not_ throw an exception since region is optional - S3Client { } + S3Client { }.use { } } @Test diff --git a/services/s3/common/test/aws/sdk/kotlin/services/s3/express/DefaultS3ExpressCredentialsProviderTest.kt b/services/s3/common/test/aws/sdk/kotlin/services/s3/express/DefaultS3ExpressCredentialsProviderTest.kt index 4b3c436714d..ceedc813b88 100644 --- a/services/s3/common/test/aws/sdk/kotlin/services/s3/express/DefaultS3ExpressCredentialsProviderTest.kt +++ b/services/s3/common/test/aws/sdk/kotlin/services/s3/express/DefaultS3ExpressCredentialsProviderTest.kt @@ -16,7 +16,9 @@ import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.time.ManualClock import kotlinx.coroutines.* import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.time.ComparableTimeMark import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes @@ -38,15 +40,15 @@ class DefaultS3ExpressCredentialsProviderTest { expiration = clock.now() + 5.minutes } - val client = TestS3Client(expectedCredentials) - - DefaultS3ExpressCredentialsProvider(timeSource, clock).use { provider -> - val credentials = provider.createSessionCredentials( - S3ExpressCredentialsCacheKey("bucket", DEFAULT_BASE_CREDENTIALS), - client, - ) - assertFalse(credentials.isExpired) - assertEquals(timeSource.markNow() + 5.minutes, credentials.expiresAt) + TestS3Client(expectedCredentials).use { client -> + DefaultS3ExpressCredentialsProvider(timeSource, clock).use { provider -> + val credentials = provider.createSessionCredentials( + S3ExpressCredentialsCacheKey("bucket", DEFAULT_BASE_CREDENTIALS), + client, + ) + assertFalse(credentials.isExpired) + assertEquals(timeSource.markNow() + 5.minutes, credentials.expiresAt) + } } } @@ -67,16 +69,17 @@ class DefaultS3ExpressCredentialsProviderTest { expiration = clock.now() + 5.minutes } - val testClient = TestS3Client(expectedCredentials) - DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes).use { provider -> - val attributes = ExecutionContext.build { - this.attributes[S3Attributes.ExpressClient] = testClient - this.attributes[S3Attributes.Bucket] = "bucket" - } + TestS3Client(expectedCredentials).use { testClient -> + DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes).use { provider -> + val attributes = ExecutionContext.build { + this.attributes[S3Attributes.ExpressClient] = testClient + this.attributes[S3Attributes.Bucket] = "bucket" + } - provider.resolve(attributes) + provider.resolve(attributes) + } + assertEquals(1, testClient.numCreateSession) } - assertEquals(1, testClient.numCreateSession) } @Test @@ -96,23 +99,23 @@ class DefaultS3ExpressCredentialsProviderTest { expiration = clock.now() + 5.minutes } - val testClient = TestS3Client(expectedCredentials) - - val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes) + TestS3Client(expectedCredentials).use { testClient -> + val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes) - val attributes = ExecutionContext.build { - this.attributes[S3Attributes.ExpressClient] = testClient - this.attributes[S3Attributes.Bucket] = "bucket" - } - provider.resolve(attributes) + val attributes = ExecutionContext.build { + this.attributes[S3Attributes.ExpressClient] = testClient + this.attributes[S3Attributes.Bucket] = "bucket" + } + provider.resolve(attributes) - // allow the async refresh to initiate before closing the provider - runBlocking { delay(500.milliseconds) } + // allow the async refresh to initiate before closing the provider + runBlocking { delay(500.milliseconds) } - provider.close() - provider.coroutineContext.job.join() + provider.close() + provider.coroutineContext.job.join() - assertEquals(1, testClient.numCreateSession) + assertEquals(1, testClient.numCreateSession) + } } @Test @@ -132,26 +135,26 @@ class DefaultS3ExpressCredentialsProviderTest { expiration = clock.now() + 5.minutes } - val testClient = TestS3Client(expectedCredentials) - - val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes) + TestS3Client(expectedCredentials).use { testClient -> + val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes) - val attributes = ExecutionContext.build { - this.attributes[S3Attributes.ExpressClient] = testClient - this.attributes[S3Attributes.Bucket] = "bucket" - } - val calls = (1..5).map { - async { provider.resolve(attributes) } - } - calls.awaitAll() + val attributes = ExecutionContext.build { + this.attributes[S3Attributes.ExpressClient] = testClient + this.attributes[S3Attributes.Bucket] = "bucket" + } + val calls = (1..5).map { + async { provider.resolve(attributes) } + } + calls.awaitAll() - // allow the async refresh to initiate before closing the provider - runBlocking { delay(500.milliseconds) } + // allow the async refresh to initiate before closing the provider + runBlocking { delay(500.milliseconds) } - provider.close() - provider.coroutineContext.job.join() + provider.close() + provider.coroutineContext.job.join() - assertEquals(1, testClient.numCreateSession) + assertEquals(1, testClient.numCreateSession) + } } @Test @@ -174,35 +177,35 @@ class DefaultS3ExpressCredentialsProviderTest { } // client should throw an exception when `ExceptionBucket` credentials are fetched, but it should be caught - val testClient = TestS3Client(expectedCredentials, throwExceptionOnBucketNamed = "ExceptionBucket") - - val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes) - val attributes = ExecutionContext.build { - this.attributes[S3Attributes.ExpressClient] = testClient - this.attributes[S3Attributes.Bucket] = "ExceptionBucket" - } - provider.resolve(attributes) + TestS3Client(expectedCredentials, throwExceptionOnBucketNamed = "ExceptionBucket").use { testClient -> + val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes) + val attributes = ExecutionContext.build { + this.attributes[S3Attributes.ExpressClient] = testClient + this.attributes[S3Attributes.Bucket] = "ExceptionBucket" + } + provider.resolve(attributes) - withTimeout(5.seconds) { - while (testClient.numCreateSession != 1) { - yield() + withTimeout(5.seconds) { + while (testClient.numCreateSession != 1) { + yield() + } } - } - assertEquals(1, testClient.numCreateSession) + assertEquals(1, testClient.numCreateSession) - attributes[S3Attributes.Bucket] = "SuccessfulBucket" - provider.resolve(attributes) + attributes[S3Attributes.Bucket] = "SuccessfulBucket" + provider.resolve(attributes) - withTimeout(5.seconds) { - while (testClient.numCreateSession != 2) { - yield() + withTimeout(5.seconds) { + while (testClient.numCreateSession != 2) { + yield() + } } - } - provider.close() - provider.coroutineContext.job.join() + provider.close() + provider.coroutineContext.job.join() - assertEquals(2, testClient.numCreateSession) + assertEquals(2, testClient.numCreateSession) + } } @Test @@ -224,7 +227,7 @@ class DefaultS3ExpressCredentialsProviderTest { val provider = DefaultS3ExpressCredentialsProvider(timeSource, clock, cache, refreshBuffer = 1.minutes) - val blockingTestS3Client = object : TestS3Client(expectedCredentials) { + class BlockingTestS3Client : TestS3Client(expectedCredentials) { override suspend fun createSession(input: CreateSessionRequest): CreateSessionResponse { delay(10.seconds) numCreateSession += 1 @@ -232,16 +235,18 @@ class DefaultS3ExpressCredentialsProviderTest { } } - val attributes = ExecutionContext.build { - this.attributes[S3Attributes.ExpressClient] = blockingTestS3Client - this.attributes[S3Attributes.Bucket] = "bucket" - } + BlockingTestS3Client().use { blockingTestS3Client -> + val attributes = ExecutionContext.build { + this.attributes[S3Attributes.ExpressClient] = blockingTestS3Client + this.attributes[S3Attributes.Bucket] = "bucket" + } - withTimeout(5.seconds) { - provider.resolve(attributes) - provider.close() + withTimeout(5.seconds) { + provider.resolve(attributes) + provider.close() + } + assertEquals(0, blockingTestS3Client.numCreateSession) } - assertEquals(0, blockingTestS3Client.numCreateSession) } /** @@ -281,5 +286,9 @@ class DefaultS3ExpressCredentialsProviderTest { } return CreateSessionResponse { credentials = expectedCredentials } } + + override fun close() { + client.close() + } } } diff --git a/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/ExpiresFieldInterceptorTest.kt b/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/ExpiresFieldInterceptorTest.kt index c910c51b877..f56f98dd381 100644 --- a/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/ExpiresFieldInterceptorTest.kt +++ b/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/ExpiresFieldInterceptorTest.kt @@ -13,6 +13,7 @@ import aws.smithy.kotlin.runtime.http.HttpBody import aws.smithy.kotlin.runtime.http.HttpStatusCode import aws.smithy.kotlin.runtime.http.response.HttpResponse import aws.smithy.kotlin.runtime.httptest.buildTestConnection +import aws.smithy.kotlin.runtime.io.use import aws.smithy.kotlin.runtime.time.Instant import kotlinx.coroutines.test.runTest import kotlin.test.Test @@ -41,15 +42,16 @@ class ExpiresFieldInterceptorTest { append("Expires", "Mon, 1 Apr 2024 00:00:00 +0000") }.build() - val s3 = newTestClient(headers = expectedHeaders) - s3.getObject( - GetObjectRequest { - bucket = "test" - key = "key" - }, - ) { - assertEquals(Instant.fromEpochSeconds(1711929600), it.expires) - assertEquals("Mon, 1 Apr 2024 00:00:00 +0000", it.expiresString) + newTestClient(headers = expectedHeaders).use { s3 -> + s3.getObject( + GetObjectRequest { + bucket = "test" + key = "key" + }, + ) { + assertEquals(Instant.fromEpochSeconds(1711929600), it.expires) + assertEquals("Mon, 1 Apr 2024 00:00:00 +0000", it.expiresString) + } } } @@ -61,15 +63,16 @@ class ExpiresFieldInterceptorTest { append("Expires", invalidExpiresField) }.build() - val s3 = newTestClient(headers = expectedHeaders) - s3.getObject( - GetObjectRequest { - bucket = "test" - key = "key" - }, - ) { - assertNull(it.expires) - assertEquals(invalidExpiresField, it.expiresString) + newTestClient(headers = expectedHeaders).use { s3 -> + s3.getObject( + GetObjectRequest { + bucket = "test" + key = "key" + }, + ) { + assertNull(it.expires) + assertEquals(invalidExpiresField, it.expiresString) + } } } } diff --git a/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/Handle200ErrorsInterceptorTest.kt b/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/Handle200ErrorsInterceptorTest.kt index e3a04168928..50650ee25dc 100644 --- a/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/Handle200ErrorsInterceptorTest.kt +++ b/services/s3/common/test/aws/sdk/kotlin/services/s3/internal/Handle200ErrorsInterceptorTest.kt @@ -15,6 +15,7 @@ import aws.smithy.kotlin.runtime.http.HttpBody import aws.smithy.kotlin.runtime.http.HttpStatusCode import aws.smithy.kotlin.runtime.http.response.HttpResponse import aws.smithy.kotlin.runtime.httptest.buildTestConnection +import aws.smithy.kotlin.runtime.io.use import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals @@ -63,23 +64,25 @@ class Handle200ErrorsInterceptorTest { @Test fun testHandle200ErrorsWithNoExpectedBody() = runTest { - val s3 = newTestClient() - val ex = assertFailsWith { - s3.deleteObject { - bucket = "test" - key = "key" + newTestClient().use { s3 -> + val ex = assertFailsWith { + s3.deleteObject { + bucket = "test" + key = "key" + } } + assertException(ex) } - assertException(ex) } @Test fun testHandle200ErrorsWithExpectedBody() = runTest { - val s3 = newTestClient() - val ex = assertFailsWith { - s3.deleteObjects { bucket = "test" } + newTestClient().use { s3 -> + val ex = assertFailsWith { + s3.deleteObjects { bucket = "test" } + } + assertException(ex) } - assertException(ex) } @Test @@ -108,15 +111,16 @@ class Handle200ErrorsInterceptorTest { rid2 """.trimIndent().encodeToByteArray() - val s3 = newTestClient(HttpStatusCode.BadRequest, payload) - val ex = assertFailsWith { - s3.deleteObjects { bucket = "test" } + newTestClient(HttpStatusCode.BadRequest, payload).use { s3 -> + val ex = assertFailsWith { + s3.deleteObjects { bucket = "test" } + } + val expectedMessage = "Please use less foos." + assertEquals("$expectedMessage, Request ID: rid, Extended request ID: rid2", ex.message) + assertEquals(expectedMessage, ex.sdkErrorMetadata.errorMessage) + assertEquals("FooError", ex.sdkErrorMetadata.errorCode) + assertEquals("rid", ex.sdkErrorMetadata.requestId) + assertEquals("rid2", ex.sdkErrorMetadata.requestId2) } - val expectedMessage = "Please use less foos." - assertEquals("$expectedMessage, Request ID: rid, Extended request ID: rid2", ex.message) - assertEquals(expectedMessage, ex.sdkErrorMetadata.errorMessage) - assertEquals("FooError", ex.sdkErrorMetadata.errorCode) - assertEquals("rid", ex.sdkErrorMetadata.requestId) - assertEquals("rid2", ex.sdkErrorMetadata.requestId2) } } diff --git a/services/s3/e2eTest/src/PaginatorTest.kt b/services/s3/e2eTest/src/PaginatorTest.kt index 865f0633b6f..df01423350e 100644 --- a/services/s3/e2eTest/src/PaginatorTest.kt +++ b/services/s3/e2eTest/src/PaginatorTest.kt @@ -38,6 +38,7 @@ class PaginatorTest { @AfterAll fun cleanup() = runBlocking { S3TestUtils.deleteBucketAndAllContents(client, testBucket) + client.close() } // ListParts has a strange pagination termination condition via [IsTerminated]. Verify it actually works correctly. diff --git a/services/s3/e2eTest/src/S3IntegrationTest.kt b/services/s3/e2eTest/src/S3IntegrationTest.kt index b0e72f80097..67a5bd75391 100644 --- a/services/s3/e2eTest/src/S3IntegrationTest.kt +++ b/services/s3/e2eTest/src/S3IntegrationTest.kt @@ -9,26 +9,24 @@ import aws.sdk.kotlin.services.s3.model.* import aws.sdk.kotlin.testing.PRINTABLE_CHARS import aws.sdk.kotlin.testing.withAllEngines import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext -import aws.smithy.kotlin.runtime.content.ByteStream -import aws.smithy.kotlin.runtime.content.asByteStream -import aws.smithy.kotlin.runtime.content.decodeToString -import aws.smithy.kotlin.runtime.content.fromFile -import aws.smithy.kotlin.runtime.content.toByteArray -import aws.smithy.kotlin.runtime.content.toByteStream +import aws.smithy.kotlin.runtime.content.* import aws.smithy.kotlin.runtime.hashing.sha256 import aws.smithy.kotlin.runtime.http.HttpException import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor import aws.smithy.kotlin.runtime.http.request.HttpRequest import aws.smithy.kotlin.runtime.testing.RandomTempFile import aws.smithy.kotlin.runtime.text.encoding.encodeToHex -import kotlinx.coroutines.* +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.TestInstance import java.io.File -import java.util.UUID +import java.util.* import kotlin.test.* import kotlin.time.Duration.Companion.seconds @@ -51,6 +49,7 @@ class S3BucketOpsIntegrationTest { @AfterAll fun cleanup() = runBlocking { S3TestUtils.deleteBucketAndAllContents(client, testBucket) + client.close() } @Test