-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: overhaul protocol test generation to use actual client #767
Changes from all commits
5feef22
6132e10
7b0aa23
040ce35
e9f6bae
c4ee2ce
1cadd2b
387184e
2ffc36b
1faa34a
849b747
478c0a2
0c97500
32ecc06
1954439
bbb3471
c7e8e72
77f303b
1558fa9
3d27a88
bcd44ad
bb46fbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// | ||
// Copyright Amazon.com Inc. or its affiliates. | ||
// All Rights Reserved. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
import struct SmithyHTTPAPI.Endpoint | ||
|
||
public struct StaticEndpointResolver<Params: EndpointsRequestContextProviding> { | ||
|
||
private let endpoint: SmithyHTTPAPI.Endpoint | ||
|
||
public init(endpoint: SmithyHTTPAPI.Endpoint) { | ||
self.endpoint = endpoint | ||
} | ||
|
||
public func resolve(params: Params) throws -> SmithyHTTPAPI.Endpoint { | ||
return endpoint | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0. | ||
*/ | ||
|
||
import protocol SmithyHTTPAPI.HTTPClient | ||
import class SmithyHTTPAPI.SdkHttpRequest | ||
import class SmithyHTTPAPI.HttpResponse | ||
import ClientRuntime | ||
|
||
public class ProtocolTestClient { | ||
public init() {} | ||
} | ||
|
||
public enum TestCheckError: Error { | ||
case actual(SdkHttpRequest) | ||
} | ||
|
||
extension ProtocolTestClient: HTTPClient { | ||
public func send(request: SdkHttpRequest) async throws -> HttpResponse { | ||
throw TestCheckError.actual(request) | ||
} | ||
} | ||
|
||
public class ProtocolTestIdempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator { | ||
public init() {} | ||
|
||
public func generateToken() -> String { | ||
return "00000000-0000-4000-8000-000000000000" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -228,6 +228,32 @@ open class HttpRequestTestBase: XCTestCase { | |
try await assertEqualHttpBody?(expected.body, actual.body) | ||
} | ||
|
||
private func checkStreamLengthEqualityIfUnseekableActualStreamHasBeenRead( | ||
expected: ByteStream, | ||
actual: ByteStream, | ||
file: StaticString = #filePath, | ||
line: UInt = #line | ||
) -> Bool { | ||
switch actual { | ||
case .stream(let actualStream): | ||
if actualStream.position == actualStream.length, !actualStream.isSeekable { | ||
switch expected { | ||
case .stream(let expectedStream): | ||
if actualStream.length == expectedStream.length { | ||
return true // Streams are considered equal | ||
} else { | ||
XCTFail("Actual stream is a different size than expected!", file: file, line: line) | ||
} | ||
default: | ||
break // This is only applicable to streams | ||
} | ||
} | ||
default: | ||
break // This is only applicable to streams | ||
} | ||
return false | ||
} | ||
|
||
public func genericAssertEqualHttpBodyData( | ||
expected: ByteStream, | ||
actual: ByteStream, | ||
|
@@ -237,6 +263,17 @@ open class HttpRequestTestBase: XCTestCase { | |
) async throws { | ||
let expectedData = try await expected.readData() | ||
let actualData = try await actual.readData() | ||
|
||
// Unseekable streams may have already been read by Signer middleware and cannot be read again | ||
// Compare stream lengths if ByteStream is a .stream and actualData is nil | ||
if checkStreamLengthEqualityIfUnseekableActualStreamHasBeenRead( | ||
expected: expected, actual: actual, file: file, line: line | ||
) { | ||
// Stream lengths were checked and comparing data will result in failure due to above conditions | ||
return | ||
} | ||
Comment on lines
+267
to
+274
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is an edge case where the SignerMiddleware has already read an unseekable stream. In which case by the time all middlewares are executed we cannot re-read the data. In that case let's check if the actual and expected stream lengths are the same. Note: This comparison should only fail if the actualStream.position == actualStream.length and !actualStream.isSeekable |
||
|
||
// Compare the data | ||
compareData(contentType: contentType, expectedData, actualData, file: file, line: line) | ||
} | ||
|
||
|
@@ -304,10 +341,17 @@ open class HttpRequestTestBase: XCTestCase { | |
return | ||
} | ||
|
||
let actualValue = actual.values(for: header.name)?.joined(separator: ", ") | ||
let actualValue = actual.values(for: header.name)? | ||
.joined(separator: ", ") | ||
.components(separatedBy: .whitespaces) | ||
.joined() | ||
XCTAssertNotNil(actualValue, file: file, line: line) | ||
|
||
let expectedValue = header.value.joined(separator: ", ") | ||
let expectedValue = header.value | ||
.joined(separator: ", ") | ||
.components(separatedBy: .whitespaces) | ||
.joined() | ||
|
||
XCTAssertEqual(actualValue, expectedValue, file: file, line: line) | ||
} | ||
} | ||
|
@@ -371,7 +415,7 @@ open class HttpRequestTestBase: XCTestCase { | |
} | ||
|
||
for expectedQueryItem in expectedQueryItems { | ||
let values = actualQueryItems.filter {$0.name == expectedQueryItem.name}.map { $0.value} | ||
let values = actualQueryItems.filter {$0.name == expectedQueryItem.name}.map { $0.value } | ||
XCTAssertNotNil( | ||
values, | ||
"expected query parameter \(expectedQueryItem.name); no values found", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ class EndpointResolverGenerator( | |
renderResolverProtocol(it) | ||
it.write("") | ||
renderResolver(it, ruleSet) | ||
renderStaticResolver(it) | ||
val inputSymbol = Symbol.builder().name("SdkHttpRequestBuilder").build() | ||
val outputSymbol = Symbol.builder().name("OperationStackOutput").build() | ||
val outputErrorSymbol = Symbol.builder().name("OperationStackError").build() | ||
|
@@ -62,4 +63,14 @@ class EndpointResolverGenerator( | |
writer.write("") | ||
writer.write("extension DefaultEndpointResolver: EndpointResolver {}") | ||
} | ||
|
||
private fun renderStaticResolver(writer: SwiftWriter) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Static Resolver will be used by tests but are also part of SRA to allow users to bypass endpoint resolution and provide their own endpoint |
||
writer.write("") | ||
writer.write( | ||
"typealias StaticEndpointResolver = \$N<EndpointParams>", | ||
ClientRuntimeTypes.Core.StaticEndpointResolver, | ||
) | ||
writer.write("") | ||
writer.write("extension StaticEndpointResolver: EndpointResolver {}") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throwing an error with the request will allow tests to do checks against the result