diff --git a/.gitignore b/.gitignore index 740eefaf436..c6ae9dc5eb6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,11 @@ build/ .idea/ __pycache__/ local.properties + # ignore generated files services/*/generated-src services/*/build.gradle.kts .kotest/ -*.klib \ No newline at end of file +*.klib +tests/codegen/smoke-tests/services/*/generated-src +tests/codegen/smoke-tests/services/*/build.gradle.kts \ No newline at end of file diff --git a/codegen/aws-sdk-codegen/build.gradle.kts b/codegen/aws-sdk-codegen/build.gradle.kts index 719643eabc8..aa404d7a4cb 100644 --- a/codegen/aws-sdk-codegen/build.gradle.kts +++ b/codegen/aws-sdk-codegen/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { api(libs.smithy.aws.cloudformation.traits) api(libs.smithy.protocol.test.traits) implementation(libs.smithy.aws.endpoints) + implementation(libs.smithy.smoke.test.traits) testImplementation(libs.junit.jupiter) testImplementation(libs.junit.jupiter.params) diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/GradleGenerator.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/GradleGenerator.kt index 0cae160ab7b..bdd10d2b459 100644 --- a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/GradleGenerator.kt +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/GradleGenerator.kt @@ -4,9 +4,17 @@ */ package aws.sdk.kotlin.codegen +import aws.sdk.kotlin.codegen.model.traits.testing.TestFailedResponseTrait +import aws.sdk.kotlin.codegen.model.traits.testing.TestSuccessResponseTrait +import aws.sdk.kotlin.codegen.smoketests.smokeTestDenyList import software.amazon.smithy.kotlin.codegen.core.* import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.model.expectShape +import software.amazon.smithy.kotlin.codegen.model.hasTrait import software.amazon.smithy.kotlin.codegen.rendering.GradleWriter +import software.amazon.smithy.kotlin.codegen.utils.topDownOperations +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.smoketests.traits.SmokeTestsTrait // TODO - would be nice to allow integrations to define custom settings in the plugin // e.g. we could then more consistently apply this integration if we could define a property like: `build.isAwsSdk: true` @@ -64,9 +72,72 @@ class GradleGenerator : KotlinIntegration { } } } + if (ctx.model.topDownOperations(ctx.settings.service).any { it.hasTrait() } && ctx.settings.sdkId !in smokeTestDenyList) { + write("") + generateSmokeTestConfig(writer, ctx) + } } val contents = writer.toString() delegator.fileManifest.writeFile("build.gradle.kts", contents) } + + private fun generateSmokeTestConfig(writer: GradleWriter, ctx: CodegenContext) { + generateSmokeTestJarTask(writer, ctx) + writer.write("") + generateSmokeTestTask(writer, ctx) + } + + /** + * Generates a gradle task to create smoke test runner JARs + */ + private fun generateSmokeTestJarTask(writer: GradleWriter, ctx: CodegenContext) { + writer.withBlock("jvm {", "}") { + withBlock("compilations {", "}") { + write("val mainPath = getByName(#S).output.classesDirs", "main") + write("val testPath = getByName(#S).output.classesDirs", "test") + withBlock("tasks {", "}") { + withBlock("register(#S) {", "}", "smokeTestJar") { + write("description = #S", "Creates smoke tests jar") + write("group = #S", "application") + write("dependsOn(build)") + write("mustRunAfter(build)") + withBlock("manifest {", "}") { + write("attributes[#S] = #S", "Main-Class", "${ctx.settings.pkg.name}.smoketests.SmokeTestsKt") + } + write("val runtimePath = configurations.getByName(#S).map { if (it.isDirectory) it else zipTree(it) }", "jvmRuntimeClasspath") + write("duplicatesStrategy = DuplicatesStrategy.EXCLUDE") + write("from(runtimePath, mainPath, testPath)") + write("archiveBaseName.set(#S)", "\${project.name}-smoketests") + } + } + } + } + } + + /** + * Generates a gradle task to run smoke tests + */ + private fun generateSmokeTestTask(writer: GradleWriter, ctx: CodegenContext) { + val hasSuccessResponseTrait = ctx.model.expectShape(ctx.settings.service).hasTrait(TestSuccessResponseTrait.ID) + val hasFailedResponseTrait = ctx.model.expectShape(ctx.settings.service).hasTrait(TestFailedResponseTrait.ID) + val inTestingEnvironment = hasFailedResponseTrait || hasSuccessResponseTrait + + /** + * E2E tests don't have sdkVersion in jar names. They're added later for publishing. + * @see SmokeTestE2ETest + */ + val jarName = if (inTestingEnvironment) "\${project.name}-smoketests.jar" else "\${project.name}-smoketests-\$sdkVersion.jar" + + writer.withBlock("tasks.register(#S) {", "}", "smokeTest") { + write("description = #S", "Runs smoke tests jar") + write("group = #S", "verification") + write("dependsOn(tasks.getByName(#S))", "smokeTestJar") + write("mustRunAfter(tasks.getByName(#S))", "smokeTestJar") + write("") + write("val sdkVersion: String by project") + write("val jarFile = file(#S)", "build/libs/$jarName") + write("classpath = files(jarFile)") + } + } } diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/model/traits/testing/SmokeTestTraits.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/model/traits/testing/SmokeTestTraits.kt new file mode 100644 index 00000000000..6805c3bad1f --- /dev/null +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/model/traits/testing/SmokeTestTraits.kt @@ -0,0 +1,27 @@ +package aws.sdk.kotlin.codegen.model.traits.testing + +import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.AnnotationTrait + +/** + * Indicates the annotated service should always return a failed response. + * IMPORTANT: This trait is intended for use in integration or E2E tests only, not in real-life smoke tests that run + * against a service endpoint. + */ +class TestFailedResponseTrait(node: ObjectNode) : AnnotationTrait(ID, node) { + companion object { + val ID: ShapeId = ShapeId.from("smithy.kotlin.traits#failedResponseTrait") + } +} + +/** + * Indicates the annotated service should always return a success response. + * IMPORTANT: This trait is intended for use in integration or E2E tests only, not in real-life smoke tests that run + * against a service endpoint. + */ +class TestSuccessResponseTrait(node: ObjectNode) : AnnotationTrait(ID, node) { + companion object { + val ID: ShapeId = ShapeId.from("smithy.kotlin.traits#successResponseTrait") + } +} diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/SmokeTestsCodegenRegionIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/SmokeTestsCodegenRegionIntegration.kt new file mode 100644 index 00000000000..a974d3ea914 --- /dev/null +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/SmokeTestsCodegenRegionIntegration.kt @@ -0,0 +1,45 @@ +package aws.sdk.kotlin.codegen.smoketests + +import software.amazon.smithy.kotlin.codegen.KotlinSettings +import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes +import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.integration.SectionWriter +import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding +import software.amazon.smithy.kotlin.codegen.model.hasTrait +import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestAdditionalEnvVars +import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestDefaultConfig +import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestRegionDefault +import software.amazon.smithy.kotlin.codegen.utils.topDownOperations +import software.amazon.smithy.model.Model +import software.amazon.smithy.smoketests.traits.SmokeTestsTrait + +/** + * Adds AWS region support to smoke tests + */ +class SmokeTestsCodegenRegionIntegration : KotlinIntegration { + override fun enabledForService(model: Model, settings: KotlinSettings): Boolean = + model.topDownOperations(settings.service).any { it.hasTrait() } + + override val sectionWriters: List + get() = listOf( + SectionWriterBinding(SmokeTestAdditionalEnvVars, envVars), + SectionWriterBinding(SmokeTestDefaultConfig, region), + SectionWriterBinding(SmokeTestRegionDefault, regionDefault), + ) + + private val envVars = SectionWriter { writer, _ -> + writer.write( + "private val regionOverride = #T.System.getenv(#S)", + RuntimeTypes.Core.Utils.PlatformProvider, + "AWS_SMOKE_TEST_REGION", + ) + } + + private val region = SectionWriter { writer, _ -> + writer.write("region = regionOverride") + } + + private val regionDefault = SectionWriter { writer, _ -> + writer.write("regionOverride ?:") + } +} diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/SmokeTestsDenyListIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/SmokeTestsDenyListIntegration.kt new file mode 100644 index 00000000000..e7de6f0119f --- /dev/null +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/SmokeTestsDenyListIntegration.kt @@ -0,0 +1,36 @@ +package aws.sdk.kotlin.codegen.smoketests + +import software.amazon.smithy.kotlin.codegen.KotlinSettings +import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.integration.SectionWriter +import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding +import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestsRunner +import software.amazon.smithy.model.Model + +/** + * Will wipe the smoke test runner file for services that are deny listed. + * + * Some services model smoke tests incorrectly and the code generated file will not compile. + */ +class SmokeTestsDenyListIntegration : KotlinIntegration { + override fun enabledForService(model: Model, settings: KotlinSettings): Boolean = + settings.sdkId in smokeTestDenyList + + override val sectionWriters: List + get() = listOf( + SectionWriterBinding(SmokeTestsRunner, smokeTestDenyListSectionWriter), + ) + + private val smokeTestDenyListSectionWriter = SectionWriter { writer, _ -> + writer.write("// Smoke tests for service deny listed until model is fixed") + } +} + +/** + * SDK ID's of services that model smoke tests incorrectly + */ +val smokeTestDenyList = setOf( + "Application Auto Scaling", + "SWF", + "WAFV2", +) diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/testing/SmokeTestFailHttpEngineIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/testing/SmokeTestFailHttpEngineIntegration.kt new file mode 100644 index 00000000000..43e5087f6be --- /dev/null +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/testing/SmokeTestFailHttpEngineIntegration.kt @@ -0,0 +1,50 @@ +package aws.sdk.kotlin.codegen.smoketests.testing + +import aws.sdk.kotlin.codegen.model.traits.testing.TestFailedResponseTrait +import aws.sdk.kotlin.codegen.model.traits.testing.TestSuccessResponseTrait +import software.amazon.smithy.kotlin.codegen.KotlinSettings +import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes +import software.amazon.smithy.kotlin.codegen.core.withBlock +import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.integration.SectionWriter +import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding +import software.amazon.smithy.kotlin.codegen.model.expectShape +import software.amazon.smithy.kotlin.codegen.model.hasTrait +import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestHttpEngineOverride +import software.amazon.smithy.kotlin.codegen.utils.topDownOperations +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.smoketests.traits.SmokeTestsTrait + +/** + * Adds [TestFailedResponseTrait] support to smoke tests + * IMPORTANT: This integration is intended for use in integration or E2E tests only, not in real-life smoke tests that run + * against a service endpoint. + */ +class SmokeTestFailHttpEngineIntegration : KotlinIntegration { + override fun enabledForService(model: Model, settings: KotlinSettings): Boolean = + model.topDownOperations(settings.service).any { it.hasTrait() } && + !model.expectShape(settings.service).hasTrait(TestSuccessResponseTrait.ID) && + model.expectShape(settings.service).hasTrait(TestFailedResponseTrait.ID) + + override val sectionWriters: List + get() = listOf( + SectionWriterBinding(SmokeTestHttpEngineOverride, httpClientOverride), + ) + + private val httpClientOverride = SectionWriter { writer, _ -> + writer.withBlock("httpClient = #T(", ")", RuntimeTypes.HttpTest.TestEngine) { + withBlock("roundTripImpl = { _, request ->", "}") { + write( + "val resp = #T(#T.BadRequest, #T.Empty, #T.Empty)", + RuntimeTypes.Http.Response.HttpResponse, + RuntimeTypes.Http.StatusCode, + RuntimeTypes.Http.Headers, + RuntimeTypes.Http.HttpBody, + ) + write("val now = #T.now()", RuntimeTypes.Core.Instant) + write("#T(request, resp, now, now)", RuntimeTypes.Http.HttpCall) + } + } + } +} diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/testing/SmokeTestSuccessHttpEngineIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/testing/SmokeTestSuccessHttpEngineIntegration.kt new file mode 100644 index 00000000000..db7b52f013b --- /dev/null +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/smoketests/testing/SmokeTestSuccessHttpEngineIntegration.kt @@ -0,0 +1,37 @@ +package aws.sdk.kotlin.codegen.smoketests.testing + +import aws.sdk.kotlin.codegen.model.traits.testing.TestFailedResponseTrait +import aws.sdk.kotlin.codegen.model.traits.testing.TestSuccessResponseTrait +import software.amazon.smithy.kotlin.codegen.KotlinSettings +import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes +import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.integration.SectionWriter +import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding +import software.amazon.smithy.kotlin.codegen.model.expectShape +import software.amazon.smithy.kotlin.codegen.model.hasTrait +import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestHttpEngineOverride +import software.amazon.smithy.kotlin.codegen.utils.topDownOperations +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.smoketests.traits.SmokeTestsTrait + +/** + * Adds [TestSuccessResponseTrait] support to smoke tests + * IMPORTANT: This integration is intended for use in integration or E2E tests only, not in real-life smoke tests that run + * against a service endpoint. + */ +class SmokeTestSuccessHttpEngineIntegration : KotlinIntegration { + override fun enabledForService(model: Model, settings: KotlinSettings): Boolean = + model.topDownOperations(settings.service).any { it.hasTrait() } && + model.expectShape(settings.service).hasTrait(TestSuccessResponseTrait.ID) && + !model.expectShape(settings.service).hasTrait(TestFailedResponseTrait.ID) + + override val sectionWriters: List + get() = listOf( + SectionWriterBinding(SmokeTestHttpEngineOverride, httpClientOverride), + ) + + private val httpClientOverride = SectionWriter { writer, _ -> + writer.write("httpClient = #T()", RuntimeTypes.HttpTest.TestEngine) + } +} diff --git a/codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration b/codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration index 876fbb45758..ad056abf01d 100644 --- a/codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +++ b/codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration @@ -43,3 +43,7 @@ aws.sdk.kotlin.codegen.customization.s3.express.SigV4S3ExpressAuthSchemeIntegrat aws.sdk.kotlin.codegen.customization.s3.express.S3ExpressIntegration aws.sdk.kotlin.codegen.customization.s3.S3ExpiresIntegration aws.sdk.kotlin.codegen.BusinessMetricsIntegration +aws.sdk.kotlin.codegen.smoketests.SmokeTestsDenyListIntegration +aws.sdk.kotlin.codegen.smoketests.SmokeTestsCodegenRegionIntegration +aws.sdk.kotlin.codegen.smoketests.testing.SmokeTestSuccessHttpEngineIntegration +aws.sdk.kotlin.codegen.smoketests.testing.SmokeTestFailHttpEngineIntegration diff --git a/settings.gradle.kts b/settings.gradle.kts index 7a29b53784f..2d6c4831ac5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -43,6 +43,8 @@ include(":services") include(":tests") include(":tests:codegen:event-stream") include(":tests:e2e-test-util") +include(":tests:codegen:smoke-tests") +include(":tests:codegen:smoke-tests:services") // generated services val File.isServiceDir: Boolean @@ -57,6 +59,13 @@ file("services").listFiles().forEach { } } +// generated services by smoke tests test suite +file("tests/codegen/smoke-tests/services").listFiles().forEach { + if (it.isServiceDir) { + include(":tests:codegen:smoke-tests:services:${it.name}") + } +} + // Service benchmarks project val benchmarkServices = listOf( // keep this list in sync with tests/benchmarks/service-benchmarks/build.gradle.kts diff --git a/tests/codegen/smoke-tests/build.gradle.kts b/tests/codegen/smoke-tests/build.gradle.kts new file mode 100644 index 00000000000..7d082ddb9b8 --- /dev/null +++ b/tests/codegen/smoke-tests/build.gradle.kts @@ -0,0 +1,124 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import aws.sdk.kotlin.gradle.codegen.dsl.generateSmithyProjections +import aws.sdk.kotlin.gradle.codegen.dsl.smithyKotlinPlugin +import aws.sdk.kotlin.gradle.codegen.smithyKotlinProjectionPath + +description = "Tests for smoke tests runners" + +plugins { + alias(libs.plugins.aws.kotlin.repo.tools.smithybuild) + alias(libs.plugins.kotlin.jvm) +} + +val projections = listOf( + Projection("successService", "smoke-tests-success.smithy", "smithy.kotlin.traits#SuccessService"), + Projection("failureService", "smoke-tests-failure.smithy", "smithy.kotlin.traits#FailureService"), +) + +configureProject() +configureProjections() +configureTasks() + +fun configureProject() { + val codegen by configurations.getting + + dependencies { + codegen(project(":codegen:aws-sdk-codegen")) + codegen(libs.smithy.cli) + codegen(libs.smithy.model) + + implementation(libs.smithy.kotlin.codegen) + + testImplementation(libs.kotlin.test) + testImplementation(gradleTestKit()) + } +} + +fun configureProjections() { + smithyBuild { + val pathToSmithyModels = "src/test/resources/" + + this@Build_gradle.projections.forEach { projection -> + projections.register(projection.name) { + imports = listOf(layout.projectDirectory.file(pathToSmithyModels + projection.modelFile).asFile.absolutePath) + smithyKotlinPlugin { + serviceShapeId = projection.serviceShapeId + packageName = "aws.sdk.kotlin.test" + packageVersion = "1.0" + buildSettings { + generateFullProject = false + generateDefaultBuildFiles = false + optInAnnotations = listOf( + "aws.smithy.kotlin.runtime.InternalApi", + "aws.sdk.kotlin.runtime.InternalSdkApi", + ) + } + } + } + } + } + + tasks.withType { + dependsOn(tasks.generateSmithyProjections) + kotlinOptions.allWarningsAsErrors = false + } +} + +fun configureTasks() { + tasks.register("stageServices") { + dependsOn(tasks.generateSmithyProjections) + + doLast { + this@Build_gradle.projections.forEach { projection -> + val projectionPath = smithyBuild.smithyKotlinProjectionPath(projection.name).get() + val destinationPath = layout.projectDirectory.asFile.absolutePath + "/services/${projection.name}" + + copy { + from("$projectionPath/src") + into("$destinationPath/generated-src") + } + copy { + from("$projectionPath/build.gradle.kts") + into(destinationPath) + } + } + } + } + + tasks.build { + dependsOn(tasks.getByName("stageServices")) + mustRunAfter(tasks.getByName("stageServices")) + } + + tasks.clean { + this@Build_gradle.projections.forEach { projection -> + delete("services/${projection.name}") + } + } + + tasks.withType { + dependsOn(tasks.getByName("stageServices")) + mustRunAfter(tasks.getByName("stageServices")) + + testLogging { + events("passed", "skipped", "failed") + exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL + showExceptions = true + showCauses = true + showStackTraces = true + } + } +} + +/** + * Holds metadata about a smithy projection + */ +data class Projection( + val name: String, + val modelFile: String, + val serviceShapeId: String, +) diff --git a/tests/codegen/smoke-tests/services/build.gradle.kts b/tests/codegen/smoke-tests/services/build.gradle.kts new file mode 100644 index 00000000000..2238ac4b0d1 --- /dev/null +++ b/tests/codegen/smoke-tests/services/build.gradle.kts @@ -0,0 +1,36 @@ +import aws.sdk.kotlin.gradle.kmp.kotlin + +plugins { + alias(libs.plugins.aws.kotlin.repo.tools.kmp) apply false +} + +// capture locally - scope issue with custom KMP plugin +val libraries = libs + +subprojects { + apply { + plugin(libraries.plugins.kotlin.multiplatform.get().pluginId) + plugin(libraries.plugins.aws.kotlin.repo.tools.kmp.get().pluginId) + } + + kotlin { + + jvm() + + sourceSets { + all { + languageSettings.optIn("kotlin.RequiresOptIn") + languageSettings.optIn("aws.smithy.kotlin.runtime.InternalApi") + languageSettings.optIn("aws.sdk.kotlin.runtime.InternalSdkApi") + } + + commonMain { + kotlin.srcDir("generated-src/main/kotlin") + } + + commonTest { + kotlin.srcDir("generated-src/test/kotlin") + } + } + } +} diff --git a/tests/codegen/smoke-tests/src/test/kotlin/SmokeTestE2ETest.kt b/tests/codegen/smoke-tests/src/test/kotlin/SmokeTestE2ETest.kt new file mode 100644 index 00000000000..d27eef61583 --- /dev/null +++ b/tests/codegen/smoke-tests/src/test/kotlin/SmokeTestE2ETest.kt @@ -0,0 +1,51 @@ +import org.gradle.testkit.runner.GradleRunner +import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SERVICE_FILTER +import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SKIP_TAGS +import java.io.File +import kotlin.test.* + +class SmokeTestE2ETest { + @Test + fun successService() { + val smokeTestRunnerOutput = runSmokeTests("successService") + + assertContains(smokeTestRunnerOutput, "ok SuccessService SuccessTest - no error expected from service") + assertContains(smokeTestRunnerOutput, "ok SuccessService SuccessTestWithTags - no error expected from service") + } + + @Test + fun failureService() { + val smokeTestRunnerOutput = runSmokeTests("failureService") + + assertContains(smokeTestRunnerOutput, "ok FailureService FailuresTest - error expected from service") + } + + @Test + fun successServiceSkipTags() { + val envVars = mapOf(SKIP_TAGS to "success") + val smokeTestRunnerOutput = runSmokeTests("successService", envVars) + + assertContains(smokeTestRunnerOutput, "ok SuccessService SuccessTest - no error expected from service") + assertContains(smokeTestRunnerOutput, "ok SuccessService SuccessTestWithTags - no error expected from service # skip") + } + + @Test + fun successServiceServiceFilter() { + val envVars = mapOf(SERVICE_FILTER to "Failure") // Only run tests for services with this SDK ID + val smokeTestRunnerOutput = runSmokeTests("successService", envVars) + + assertContains(smokeTestRunnerOutput, "ok SuccessService SuccessTest - no error expected from service # skip") + assertContains(smokeTestRunnerOutput, "ok SuccessService SuccessTestWithTags - no error expected from service # skip") + } +} + +private fun runSmokeTests(service: String, envVars: Map = emptyMap()): String { + val sdkRootDir = System.getProperty("user.dir") + "/../../../" + val runner = GradleRunner.create() + .withProjectDir(File(sdkRootDir)) + .withArguments(":tests:codegen:smoke-tests:services:$service:smokeTest") + .withEnvironment(envVars) + .build() + + return runner.output +} diff --git a/tests/codegen/smoke-tests/src/test/resources/smoke-tests-failure.smithy b/tests/codegen/smoke-tests/src/test/resources/smoke-tests-failure.smithy new file mode 100644 index 00000000000..0a903a77303 --- /dev/null +++ b/tests/codegen/smoke-tests/src/test/resources/smoke-tests-failure.smithy @@ -0,0 +1,62 @@ +$version: "2" +namespace smithy.kotlin.traits + +use aws.protocols#awsJson1_0 +use aws.api#service +use smithy.test#smokeTests +use smithy.rules#endpointRuleSet + +@trait(selector: "service") +structure failedResponseTrait { } + +@failedResponseTrait +@awsJson1_0 +@service(sdkId: "Failure") +@endpointRuleSet( + version: "1.0", + parameters: {}, + rules: [ + { + "type": "endpoint", + "conditions": [], + "endpoint": { + "url": "https://static.endpoint" + } + } + ] +) +service FailureService { + version: "1.0.0", + operations: [ TestOperation ], +} + +@smokeTests( + [ + { + id: "FailuresTest" + params: {bar: "2"} + expect: { + failure: {} + } + vendorParamsShape: AwsVendorParams, + vendorParams: { + region: "eu-central-1" + } + } + ] +) +operation TestOperation { + input := { + bar: String + } + errors: [ + InvalidMessageError + ] +} + +@error("client") +structure InvalidMessageError {} + +structure AwsVendorParams { + region: String +} \ No newline at end of file diff --git a/tests/codegen/smoke-tests/src/test/resources/smoke-tests-success.smithy b/tests/codegen/smoke-tests/src/test/resources/smoke-tests-success.smithy new file mode 100644 index 00000000000..45a8f425881 --- /dev/null +++ b/tests/codegen/smoke-tests/src/test/resources/smoke-tests-success.smithy @@ -0,0 +1,76 @@ +$version: "2" +namespace smithy.kotlin.traits + +use aws.protocols#awsJson1_0 +use aws.api#service +use smithy.test#smokeTests +use smithy.rules#endpointRuleSet + +@trait(selector: "service") +structure successResponseTrait { } + +@successResponseTrait +@awsJson1_0 +@service(sdkId: "Success") +@endpointRuleSet( + version: "1.0", + parameters: {}, + rules: [ + { + "type": "endpoint", + "conditions": [], + "endpoint": { + "url": "https://static.endpoint" + } + } + ] +) +service SuccessService { + version: "1.0.0", + operations: [ TestOperation ], +} + +@smokeTests( + [ + { + id: "SuccessTest" + params: {bar: "2"} + expect: { + success: {} + } + vendorParamsShape: AwsVendorParams, + vendorParams: { + region: "eu-central-1" + } + }, + { + id: "SuccessTestWithTags" + params: {bar: "2"} + tags: [ + "success" + ] + expect: { + success: {} + } + vendorParamsShape: AwsVendorParams, + vendorParams: { + region: "eu-central-1" + } + } + ] +) +operation TestOperation { + input := { + bar: String + } + errors: [ + InvalidMessageError + ] +} + +@error("client") +structure InvalidMessageError {} + +structure AwsVendorParams { + region: String +} \ No newline at end of file