Unverified Commit 8abc9463 authored by Nate McMaster (AWS)'s avatar Nate McMaster (AWS) Committed by GitHub
Browse files

Update OperationInputTestDecorator to find operation shape by name (#2782)

## Motivation and Context
Addresses https://github.com/awslabs/smithy-rs/issues/2767



This fixes an issue for users who write endpoint tests that rely on an
operation and service shape from different namespaces.

## Description
Instead of assuming operation namespace matches service namespace, 

## Testing
`./gradlew :aws:sdk-codegen:test`


----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._

---------

Co-authored-by: default avatarZelda Hessler <zhessler@amazon.com>
parent 735b6351
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -611,6 +611,12 @@ references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"]
meta = { "breaking" = true, "tada" = false, "bug" = false }
author = "hlbarber"

[[smithy-rs]]
message = "Fix bug in client generation when using smithy.rules#endpointTests and operation and service shapes are in different namespaces."
author = "mcmasn-amzn"
references = ["smithy-rs#2767"]
meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" }

[[smithy-rs]]
message = "The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`."
references = ["smithy-rs#2783"]
+4 −4
Original line number Diff line number Diff line
@@ -122,9 +122,6 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test:
    private val model = ctx.model
    private val instantiator = ClientInstantiator(ctx)

    private fun EndpointTestOperationInput.operationId() =
        ShapeId.fromOptionalNamespace(ctx.serviceShape.id.namespace, operationName)

    /** the Rust SDK doesn't support SigV4a — search  endpoint.properties.authSchemes[].name */
    private fun EndpointTestCase.isSigV4a() =
        expect.endpoint.orNull()?.properties?.get("authSchemes")?.asArrayNode()?.orNull()
@@ -183,7 +180,7 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test:
    private fun operationInvocation(testOperationInput: EndpointTestOperationInput) = writable {
        rust("client.${testOperationInput.operationName.toSnakeCase()}()")
        val operationInput =
            model.expectShape(testOperationInput.operationId(), OperationShape::class.java).inputShape(model)
            model.expectShape(ctx.operationId(testOperationInput), OperationShape::class.java).inputShape(model)
        testOperationInput.operationParams.members.forEach { (key, value) ->
            val member = operationInput.expectMember(key.value)
            rustTemplate(
@@ -217,3 +214,6 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test:
        }
    }
}

fun ClientCodegenContext.operationId(testOperationInput: EndpointTestOperationInput): ShapeId =
    this.serviceShape.allOperations.first { it.name == testOperationInput.operationName }
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package software.amazon.smithy.rustsdk

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import software.amazon.smithy.model.Model
import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput
import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rustsdk.endpoints.operationId

class OperationInputTestGeneratorTests {
    @Test
    fun `finds operation shape by name`() {
        val prefix = "\$version: \"2\""
        val operationModel = """
            $prefix
            namespace operations
            
            operation Ping {}
        """.trimIndent()
        val serviceModel = """
            $prefix
            namespace service
            
            use operations#Ping
            
            service MyService {
                operations: [Ping]
            }
        """.trimIndent()

        val model = Model.assembler()
            .discoverModels()
            .addUnparsedModel("operation.smithy", operationModel)
            .addUnparsedModel("main.smithy", serviceModel)
            .assemble()
            .unwrap()

        val context = testClientCodegenContext(model)
        val testOperationInput = EndpointTestOperationInput.builder()
            .operationName("Ping")
            .build()

        val operationId = context.operationId(testOperationInput)
        assertEquals("operations#Ping", operationId.toString())
    }

    @Test
    fun `fails for operation name not found`() {
        val model = """
            namespace test
            operation Ping {}
            service MyService {
                operations: [Ping]
            }
        """.trimIndent().asSmithyModel()

        val context = testClientCodegenContext(model)
        val testOperationInput = EndpointTestOperationInput.builder()
            .operationName("Pong")
            .build()

        assertThrows<NoSuchElementException> { context.operationId(testOperationInput) }
    }
}