Skip to content

Commit 7b266f8

Browse files
authored
chore: Use client to perform HTTP response tests (#1018)
1 parent 7a9a699 commit 7b266f8

File tree

9 files changed

+345
-114
lines changed

9 files changed

+345
-114
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import struct AwsCommonRuntimeKit.CommonRuntimeKit
9+
10+
/// Sets up CRT-related shared resources such as the global allocator, event loops, etc.
11+
///
12+
/// Calls to CRT functions may crash the SDK if `CommonRuntimeKit.initialize()` is not called first.
13+
///
14+
/// This function may safely be called multiple times.
15+
public func initialize() { CommonRuntimeKit.initialize() }
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import protocol SmithyHTTPAPI.HTTPClient
9+
import class SmithyHTTPAPI.HTTPRequest
10+
import class SmithyHTTPAPI.HTTPResponse
11+
12+
public class ProtocolResponseTestClient {
13+
let httpResponse: HTTPResponse
14+
15+
public init(httpResponse: HTTPResponse) {
16+
self.httpResponse = httpResponse
17+
}
18+
}
19+
20+
extension ProtocolResponseTestClient: HTTPClient {
21+
22+
public func send(request: HTTPRequest) async throws -> HTTPResponse {
23+
httpResponse
24+
}
25+
}

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ open class HttpProtocolServiceClient(
5252

5353
open fun renderInitFunction() {
5454
writer.openBlock("public required init(config: \$L) {", "}", serviceConfig.typeName) {
55+
writer.write("\$N()", ClientRuntimeTypes.Core.initialize)
5556
writer.write(
5657
"client = \$N(engine: config.httpClientEngine, config: config.httpClientConfiguration)",
5758
ClientRuntimeTypes.Http.SdkHttpClient,

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestErrorGenerator.kt

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,59 +10,47 @@ import software.amazon.smithy.protocol.traits.Rpcv2CborTrait
1010
import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase
1111
import software.amazon.smithy.swift.codegen.SwiftDependency
1212
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.AWSProtocol
13-
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.ResponseErrorClosureUtils
1413
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WireProtocol
1514
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.awsProtocol
1615
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol
1716
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol
18-
import software.amazon.smithy.swift.codegen.model.toUpperCamelCase
17+
import software.amazon.smithy.swift.codegen.model.toLowerCamelCase
1918
import software.amazon.smithy.swift.codegen.swiftmodules.SmithyHTTPAPITypes
19+
import software.amazon.smithy.swift.codegen.swiftmodules.SwiftTypes
2020
import java.util.Base64
2121

2222
open class HttpProtocolUnitTestErrorGenerator protected constructor(
2323
builder: Builder,
2424
) : HttpProtocolUnitTestResponseGenerator(builder) {
2525
val error: Shape = builder.error ?: throw CodegenException("builder did not set an error shape")
26-
override val outputShape: Shape? = error
2726

2827
override fun renderTestBody(test: HttpResponseTestCase) {
29-
outputShape?.let {
30-
val operationErrorType = "${operation.toUpperCamelCase()}OutputError"
31-
writer.openBlock("do {", "} catch {") {
32-
renderBuildHttpResponse(test)
33-
writer.write("")
34-
renderInitOperationError(operationErrorType)
35-
writer.write("")
36-
renderCompareActualAndExpectedErrors(test, it, operationErrorType)
37-
writer.write("")
38-
}
39-
writer.indent()
40-
writer.write("XCTFail(error.localizedDescription)")
41-
writer.dedent()
42-
writer.write("}")
43-
}
28+
renderBuildHttpResponse(test)
29+
writer.write("")
30+
renderActualOutput()
31+
writer.write("")
32+
renderCompareActualAndExpectedErrors(test, error)
4433
}
4534

46-
private fun renderInitOperationError(operationErrorType: String) {
47-
val operationErrorVariableName = operationErrorType.replaceFirstChar { it.lowercase() }
48-
val responseErrorClosure = ResponseErrorClosureUtils(ctx, writer, operation).render()
49-
writer.addImport(SwiftDependency.SMITHY_READ_WRITE.target)
50-
writer.write(
51-
"let \$L = try await \$L(httpResponse)",
52-
operationErrorVariableName,
53-
responseErrorClosure,
54-
)
35+
override fun captureResponse() {
36+
writer.write("var operationError: \$N?", SwiftTypes.Error)
37+
writer.write("do {")
38+
writer.indent {
39+
writer.write("_ = try await client.\$L(input: input)", operation.toLowerCamelCase())
40+
writer.write("XCTFail(\"Request should have failed\")")
41+
}
42+
writer.openBlock("} catch {", "}") {
43+
writer.write("operationError = error")
44+
}
5545
}
5646

5747
private fun renderCompareActualAndExpectedErrors(
5848
test: HttpResponseTestCase,
5949
errorShape: Shape,
60-
operationErrorType: String,
6150
) {
62-
val operationErrorVariableName = operationErrorType.replaceFirstChar { it.lowercase() }
6351
val errorType = symbolProvider.toSymbol(errorShape).name
6452

65-
writer.openBlock("if let actual = \$L as? \$L {", "} else {", operationErrorVariableName, errorType) {
53+
writer.openBlock("if let actual = operationError as? \$L {", "} else {", errorType) {
6654
renderExpectedOutput(test, errorShape)
6755
renderAssertions(test, errorShape)
6856
}

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolUnitTestResponseGenerator.kt

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,31 @@
44
*/
55
package software.amazon.smithy.swift.codegen.integration
66

7-
import software.amazon.smithy.codegen.core.Symbol
87
import software.amazon.smithy.model.shapes.Shape
8+
import software.amazon.smithy.model.shapes.ShapeType
99
import software.amazon.smithy.model.shapes.StructureShape
1010
import software.amazon.smithy.model.traits.DefaultTrait
1111
import software.amazon.smithy.model.traits.HttpHeaderTrait
12+
import software.amazon.smithy.model.traits.HttpLabelTrait
1213
import software.amazon.smithy.model.traits.HttpPayloadTrait
1314
import software.amazon.smithy.model.traits.HttpPrefixHeadersTrait
1415
import software.amazon.smithy.protocol.traits.Rpcv2CborTrait
1516
import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase
17+
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait
1618
import software.amazon.smithy.swift.codegen.ShapeValueGenerator
1719
import software.amazon.smithy.swift.codegen.hasStreamingMember
1820
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.AWSProtocol
19-
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.ResponseClosureUtils
2021
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.WireProtocol
2122
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.awsProtocol
2223
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.requestWireProtocol
2324
import software.amazon.smithy.swift.codegen.integration.serde.readwrite.responseWireProtocol
2425
import software.amazon.smithy.swift.codegen.model.RecursiveShapeBoxer
2526
import software.amazon.smithy.swift.codegen.model.hasTrait
27+
import software.amazon.smithy.swift.codegen.model.toLowerCamelCase
28+
import software.amazon.smithy.swift.codegen.swiftmodules.FoundationTypes
29+
import software.amazon.smithy.swift.codegen.swiftmodules.SmithyHTTPAPITypes
2630
import software.amazon.smithy.swift.codegen.swiftmodules.SmithyStreamsTypes
31+
import software.amazon.smithy.swift.codegen.swiftmodules.SmithyTestUtilTypes
2732
import java.util.Base64
2833

2934
/**
@@ -34,6 +39,14 @@ open class HttpProtocolUnitTestResponseGenerator protected constructor(
3439
) : HttpProtocolUnitTestGenerator<HttpResponseTestCase>(builder) {
3540
override val baseTestClassName = "HttpResponseTestBase"
3641

42+
protected open val inputShape: Shape?
43+
get() {
44+
return operation.input
45+
.map {
46+
model.expectShape(it)
47+
}.orElse(null)
48+
}
49+
3750
protected open val outputShape: Shape?
3851
get() {
3952
return operation.output
@@ -44,10 +57,9 @@ open class HttpProtocolUnitTestResponseGenerator protected constructor(
4457

4558
override fun renderTestBody(test: HttpResponseTestCase) {
4659
outputShape?.let {
47-
val symbol = symbolProvider.toSymbol(it)
4860
renderBuildHttpResponse(test)
4961
writer.write("")
50-
renderActualOutput(symbol)
62+
renderActualOutput()
5163
writer.write("")
5264
renderExpectedOutput(test, it)
5365
writer.write("")
@@ -163,9 +175,69 @@ open class HttpProtocolUnitTestResponseGenerator protected constructor(
163175
}
164176
}
165177

166-
private fun renderActualOutput(outputStruct: Symbol) {
167-
val responseClosure = ResponseClosureUtils(ctx, writer, operation).render()
168-
writer.write("let actual: \$N = try await \$L(httpResponse)", outputStruct, responseClosure)
178+
fun renderActualOutput() {
179+
val clientName = "${ctx.settings.sdkId}Client"
180+
val region = "us-west-2"
181+
182+
// Create a client config. Use a dummy for:
183+
// - credential resolver
184+
// - endpoint resolver (unless the test has endpoint rules)
185+
// - HTTP client engine; a mock that returns the test's HTTPResponse is used
186+
writer.openBlock("let config = try await \$L.Config(", ")", clientName) {
187+
writer.write("awsCredentialIdentityResolver: try \$N(),", SmithyTestUtilTypes.dummyIdentityResolver)
188+
writer.write("region: \$S,", region)
189+
writer.write("signingRegion: \$S,", region)
190+
if (!ctx.service.hasTrait<EndpointRuleSetTrait>()) {
191+
writer.openBlock(
192+
"endpointResolver: StaticEndpointResolver(endpoint: try \$N(",
193+
")),",
194+
SmithyHTTPAPITypes.Endpoint,
195+
) {
196+
writer.write("urlString: \"https://example.com\"")
197+
}
198+
}
199+
writer.write("httpClientEngine: ProtocolResponseTestClient(httpResponse: httpResponse)")
200+
}
201+
writer.write("")
202+
203+
// Create a client with the config
204+
writer.write("let client = \$L(config: config)", clientName)
205+
writer.write("")
206+
207+
// If input has any httpLabel-bound members, these must be filled so the request can succeed
208+
val inputArgs = mutableListOf<String>()
209+
for (member in inputShape?.members() ?: listOf()) {
210+
if (member.hasTrait<HttpLabelTrait>()) {
211+
val memberName = ctx.symbolProvider.toMemberName(member)
212+
val target = model.expectShape(member.target)
213+
val defaultArg =
214+
when (target.type) {
215+
ShapeType.STRING -> "\"test\""
216+
ShapeType.BOOLEAN -> "false"
217+
ShapeType.TIMESTAMP -> writer.format("\$N()", FoundationTypes.Date)
218+
else -> "0" // only other allowed types are numbers
219+
}
220+
val arg = writer.format("\$L: \$L", memberName, defaultArg)
221+
inputArgs.add(arg)
222+
}
223+
}
224+
225+
// Create the input, adding params if any
226+
writer.write(
227+
"let input = \$L(\$L)",
228+
ctx.symbolProvider.toSymbol(inputShape),
229+
inputArgs.joinToString(", "),
230+
)
231+
writer.write("")
232+
captureResponse()
233+
writer.write("")
234+
}
235+
236+
open fun captureResponse() {
237+
writer.write(
238+
"let actual = try await client.\$L(input: input)",
239+
operation.toLowerCamelCase(),
240+
)
169241
}
170242

171243
protected fun renderExpectedOutput(

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/swiftmodules/ClientRuntimeTypes.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ object ClientRuntimeTypes {
9090
val InterceptorProvider = runtimeSymbol("InterceptorProvider", SwiftDeclaration.PROTOCOL)
9191
val HttpInterceptorProvider = runtimeSymbol("HttpInterceptorProvider", SwiftDeclaration.PROTOCOL)
9292
val SDKLoggingSystem = runtimeSymbol("SDKLoggingSystem", SwiftDeclaration.CLASS)
93+
val initialize = runtimeSymbol("initialize", SwiftDeclaration.FUNC)
9394
}
9495

9596
object Composite {

smithy-swift-codegen/src/test/kotlin/software/amazon/smithy/swift/codegen/HttpProtocolClientGeneratorTests.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public class RestJsonProtocolClient: ClientRuntime.Client {
2525
let serviceName = "Rest Json Protocol"
2626
2727
public required init(config: RestJsonProtocolClient.RestJsonProtocolClientConfiguration) {
28+
ClientRuntime.initialize()
2829
client = ClientRuntime.SdkHttpClient(engine: config.httpClientEngine, config: config.httpClientConfiguration)
2930
self.config = config
3031
}

0 commit comments

Comments
 (0)