Files
Donald Ness 699ada1724 Add guards for code that relies on _Concurrency (#237)
This allows Xcode 13 to still compile on macOS 11 which does not have symbols for Concurrency yet
See swift-server/swift-service-lifecycle#110
2021-10-04 17:20:07 -07:00

324 lines
12 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaRuntimeCore
import NIO
import XCTest
class LambdaHandlerTest: XCTestCase {
// MARK: - Callback
func testCallbackSuccess() {
let server = MockLambdaServer(behavior: Behavior())
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: LambdaHandler {
typealias In = String
typealias Out = String
func handle(context: Lambda.Context, event: String, callback: (Result<String, Error>) -> Void) {
callback(.success(event))
}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, handler: Handler())
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
func testVoidCallbackSuccess() {
let server = MockLambdaServer(behavior: Behavior(result: .success(nil)))
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: LambdaHandler {
typealias In = String
typealias Out = Void
func handle(context: Lambda.Context, event: String, callback: (Result<Void, Error>) -> Void) {
callback(.success(()))
}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, handler: Handler())
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
func testCallbackFailure() {
let server = MockLambdaServer(behavior: Behavior(result: .failure(TestError("boom"))))
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: LambdaHandler {
typealias In = String
typealias Out = String
func handle(context: Lambda.Context, event: String, callback: (Result<String, Error>) -> Void) {
callback(.failure(TestError("boom")))
}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, handler: Handler())
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
#if compiler(>=5.5) && canImport(_Concurrency)
// MARK: - AsyncLambdaHandler
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
func testAsyncHandlerSuccess() {
let server = MockLambdaServer(behavior: Behavior())
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: AsyncLambdaHandler {
typealias In = String
typealias Out = String
init(context: Lambda.InitializationContext) {}
func handle(event: String, context: Lambda.Context) async throws -> String {
event
}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, factory: Handler.init)
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
func testVoidAsyncHandlerSuccess() {
let server = MockLambdaServer(behavior: Behavior(result: .success(nil)))
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: AsyncLambdaHandler {
typealias In = String
typealias Out = Void
init(context: Lambda.InitializationContext) {}
func handle(event: String, context: Lambda.Context) async throws {}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, factory: Handler.init)
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
func testAsyncHandlerFailure() {
let server = MockLambdaServer(behavior: Behavior(result: .failure(TestError("boom"))))
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: AsyncLambdaHandler {
typealias In = String
typealias Out = String
init(context: Lambda.InitializationContext) {}
func handle(event: String, context: Lambda.Context) async throws -> String {
throw TestError("boom")
}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, factory: Handler.init)
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
#endif
// MARK: - EventLoop
func testEventLoopSuccess() {
let server = MockLambdaServer(behavior: Behavior())
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: EventLoopLambdaHandler {
typealias In = String
typealias Out = String
func handle(context: Lambda.Context, event: String) -> EventLoopFuture<String> {
context.eventLoop.makeSucceededFuture(event)
}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, handler: Handler())
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
func testVoidEventLoopSuccess() {
let server = MockLambdaServer(behavior: Behavior(result: .success(nil)))
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: EventLoopLambdaHandler {
typealias In = String
typealias Out = Void
func handle(context: Lambda.Context, event: String) -> EventLoopFuture<Void> {
context.eventLoop.makeSucceededFuture(())
}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, handler: Handler())
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
func testEventLoopFailure() {
let server = MockLambdaServer(behavior: Behavior(result: .failure(TestError("boom"))))
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: EventLoopLambdaHandler {
typealias In = String
typealias Out = String
func handle(context: Lambda.Context, event: String) -> EventLoopFuture<String> {
context.eventLoop.makeFailedFuture(TestError("boom"))
}
}
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration, handler: Handler())
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
// MARK: - Closure
func testClosureSuccess() {
let server = MockLambdaServer(behavior: Behavior())
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration) { (_, event: String, callback) in
callback(.success(event))
}
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
func testVoidClosureSuccess() {
let server = MockLambdaServer(behavior: Behavior(result: .success(nil)))
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result = Lambda.run(configuration: configuration) { (_, _: String, callback) in
callback(.success(()))
}
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
func testClosureFailure() {
let server = MockLambdaServer(behavior: Behavior(result: .failure(TestError("boom"))))
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
let maxTimes = Int.random(in: 1 ... 10)
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
let result: Result<Int, Error> = Lambda.run(configuration: configuration) { (_, _: String, callback: (Result<String, Error>) -> Void) in
callback(.failure(TestError("boom")))
}
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
}
func testBootstrapFailure() {
let server = MockLambdaServer(behavior: FailedBootstrapBehavior())
XCTAssertNoThrow(try server.start().wait())
defer { XCTAssertNoThrow(try server.stop().wait()) }
struct Handler: LambdaHandler {
typealias In = String
typealias Out = String
init(context: Lambda.InitializationContext) throws {
throw TestError("kaboom")
}
func handle(context: Lambda.Context, event: String, callback: (Result<String, Error>) -> Void) {
callback(.failure(TestError("should not be called")))
}
}
let result = Lambda.run(factory: Handler.init)
assertLambdaLifecycleResult(result, shouldFailWithError: TestError("kaboom"))
}
}
private struct Behavior: LambdaServerBehavior {
let requestId: String
let event: String
let result: Result<String?, TestError>
init(requestId: String = UUID().uuidString, event: String = "hello", result: Result<String?, TestError> = .success("hello")) {
self.requestId = requestId
self.event = event
self.result = result
}
func getInvocation() -> GetInvocationResult {
.success((requestId: self.requestId, event: self.event))
}
func processResponse(requestId: String, response: String?) -> Result<Void, ProcessResponseError> {
XCTAssertEqual(self.requestId, requestId, "expecting requestId to match")
switch self.result {
case .success(let expected):
XCTAssertEqual(expected, response, "expecting response to match")
return .success(())
case .failure:
XCTFail("unexpected to fail, but succeeded with: \(response ?? "undefined")")
return .failure(.internalServerError)
}
}
func processError(requestId: String, error: ErrorResponse) -> Result<Void, ProcessErrorError> {
XCTAssertEqual(self.requestId, requestId, "expecting requestId to match")
switch self.result {
case .success:
XCTFail("unexpected to succeed, but failed with: \(error)")
return .failure(.internalServerError)
case .failure(let expected):
XCTAssertEqual(expected.description, error.errorMessage, "expecting error to match")
return .success(())
}
}
func processInitError(error: ErrorResponse) -> Result<Void, ProcessErrorError> {
XCTFail("should not report init error")
return .failure(.internalServerError)
}
}