mirror of
https://github.com/swift-server/swift-aws-lambda-runtime.git
synced 2026-05-03 07:22:27 +00:00
Lambda factory as a protocol requirement. (#244)
This commit is contained in:
@@ -20,13 +20,16 @@ import NIOCore
|
||||
// `EventLoopLambdaHandler` does not offload the Lambda processing to a separate thread
|
||||
// while the closure-based handlers do.
|
||||
|
||||
struct MyLambda: EventLoopLambdaHandler {
|
||||
@main
|
||||
struct BenchmarkHandler: EventLoopLambdaHandler {
|
||||
typealias Event = String
|
||||
typealias Output = String
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Self> {
|
||||
context.eventLoop.makeSucceededFuture(BenchmarkHandler())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
|
||||
context.eventLoop.makeSucceededFuture("hello, world!")
|
||||
}
|
||||
}
|
||||
|
||||
Lambda.run { $0.eventLoop.makeSucceededFuture(MyLambda()) }
|
||||
+5
-2
@@ -20,13 +20,16 @@ import NIO
|
||||
// `EventLoopLambdaHandler` does not offload the Lambda processing to a separate thread
|
||||
// while the closure-based handlers do.
|
||||
|
||||
@main
|
||||
struct BenchmarkHandler: EventLoopLambdaHandler {
|
||||
typealias Event = String
|
||||
typealias Output = String
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Self> {
|
||||
context.eventLoop.makeSucceededFuture(BenchmarkHandler())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
|
||||
context.eventLoop.makeSucceededFuture("hello, world!")
|
||||
}
|
||||
}
|
||||
|
||||
Lambda.run { $0.eventLoop.makeSucceededFuture(BenchmarkHandler()) }
|
||||
@@ -24,27 +24,6 @@ import NIOCore
|
||||
import NIOPosix
|
||||
|
||||
public enum Lambda {
|
||||
public typealias Handler = ByteBufferLambdaHandler
|
||||
|
||||
/// `ByteBufferLambdaHandler` factory.
|
||||
///
|
||||
/// A function that takes a `InitializationContext` and returns an `EventLoopFuture` of a `ByteBufferLambdaHandler`
|
||||
public typealias HandlerFactory = (InitializationContext) -> EventLoopFuture<Handler>
|
||||
|
||||
/// Run a Lambda defined by implementing the `LambdaHandler` protocol provided via a `LambdaHandlerFactory`.
|
||||
/// Use this to initialize all your resources that you want to cache between invocations. This could be database connections and HTTP clients for example.
|
||||
/// It is encouraged to use the given `EventLoop`'s conformance to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - factory: A `ByteBufferLambdaHandler` factory.
|
||||
///
|
||||
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
|
||||
public static func run(_ factory: @escaping HandlerFactory) {
|
||||
if case .failure(let error) = self.run(factory: factory) {
|
||||
fatalError("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility to access/read environment variables
|
||||
public static func env(_ name: String) -> String? {
|
||||
guard let value = getenv(name) else {
|
||||
@@ -53,30 +32,27 @@ public enum Lambda {
|
||||
return String(cString: value)
|
||||
}
|
||||
|
||||
#if compiler(>=5.5) && canImport(_Concurrency)
|
||||
// for testing and internal use
|
||||
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
|
||||
internal static func run<Handler: LambdaHandler>(configuration: Configuration = .init(), handlerType: Handler.Type) -> Result<Int, Error> {
|
||||
self.run(configuration: configuration, factory: { context -> EventLoopFuture<ByteBufferLambdaHandler> in
|
||||
let promise = context.eventLoop.makePromise(of: ByteBufferLambdaHandler.self)
|
||||
promise.completeWithTask {
|
||||
try await Handler(context: context)
|
||||
}
|
||||
return promise.futureResult
|
||||
})
|
||||
}
|
||||
#endif
|
||||
|
||||
// for testing and internal use
|
||||
internal static func run(configuration: Configuration = .init(), factory: @escaping HandlerFactory) -> Result<Int, Error> {
|
||||
let _run = { (configuration: Configuration, factory: @escaping HandlerFactory) -> Result<Int, Error> in
|
||||
/// Run a Lambda defined by implementing the ``ByteBufferLambdaHandler`` protocol.
|
||||
/// The Runtime will manage the Lambdas application lifecycle automatically. It will invoke the
|
||||
/// ``ByteBufferLambdaHandler/makeHandler(context:)`` to create a new Handler.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - configuration: A Lambda runtime configuration object
|
||||
/// - handlerType: The Handler to create and invoke.
|
||||
///
|
||||
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
|
||||
internal static func run<Handler: ByteBufferLambdaHandler>(
|
||||
configuration: Configuration = .init(),
|
||||
handlerType: Handler.Type
|
||||
) -> Result<Int, Error> {
|
||||
let _run = { (configuration: Configuration) -> Result<Int, Error> in
|
||||
Backtrace.install()
|
||||
var logger = Logger(label: "Lambda")
|
||||
logger.logLevel = configuration.general.logLevel
|
||||
|
||||
var result: Result<Int, Error>!
|
||||
MultiThreadedEventLoopGroup.withCurrentThreadAsEventLoop { eventLoop in
|
||||
let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, configuration: configuration, factory: factory)
|
||||
let runtime = LambdaRuntime<Handler>(eventLoop: eventLoop, logger: logger, configuration: configuration)
|
||||
#if DEBUG
|
||||
let signalSource = trap(signal: configuration.lifecycle.stopSignal) { signal in
|
||||
logger.info("intercepted signal: \(signal)")
|
||||
@@ -108,16 +84,16 @@ public enum Lambda {
|
||||
if Lambda.env("LOCAL_LAMBDA_SERVER_ENABLED").flatMap(Bool.init) ?? false {
|
||||
do {
|
||||
return try Lambda.withLocalServer {
|
||||
_run(configuration, factory)
|
||||
_run(configuration)
|
||||
}
|
||||
} catch {
|
||||
return .failure(error)
|
||||
}
|
||||
} else {
|
||||
return _run(configuration, factory)
|
||||
return _run(configuration)
|
||||
}
|
||||
#else
|
||||
return _run(configuration, factory)
|
||||
return _run(configuration)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ import NIOCore
|
||||
|
||||
extension Lambda {
|
||||
/// Lambda runtime initialization context.
|
||||
/// The Lambda runtime generates and passes the `InitializationContext` to the Lambda factory as an argument.
|
||||
/// The Lambda runtime generates and passes the `InitializationContext` to the Handlers
|
||||
/// ``ByteBufferLambdaHandler/makeHandler(context:)`` or ``LambdaHandler/init(context:)``
|
||||
/// as an argument.
|
||||
public struct InitializationContext {
|
||||
/// `Logger` to log with
|
||||
///
|
||||
|
||||
@@ -18,7 +18,13 @@ import NIOCore
|
||||
// MARK: - LambdaHandler
|
||||
|
||||
#if compiler(>=5.5) && canImport(_Concurrency)
|
||||
/// Strongly typed, processing protocol for a Lambda that takes a user defined `Event` and returns a user defined `Output` async.
|
||||
/// Strongly typed, processing protocol for a Lambda that takes a user defined
|
||||
/// ``EventLoopLambdaHandler/Event`` and returns a user defined
|
||||
/// ``EventLoopLambdaHandler/Output`` asynchronously.
|
||||
///
|
||||
/// - note: Most users should implement this protocol instead of the lower
|
||||
/// level protocols ``EventLoopLambdaHandler`` and
|
||||
/// ``ByteBufferLambdaHandler``.
|
||||
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
|
||||
public protocol LambdaHandler: EventLoopLambdaHandler {
|
||||
/// The Lambda initialization method
|
||||
@@ -42,6 +48,14 @@ public protocol LambdaHandler: EventLoopLambdaHandler {
|
||||
|
||||
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
|
||||
extension LambdaHandler {
|
||||
public static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Self> {
|
||||
let promise = context.eventLoop.makePromise(of: Self.self)
|
||||
promise.completeWithTask {
|
||||
try await Self(context: context)
|
||||
}
|
||||
return promise.futureResult
|
||||
}
|
||||
|
||||
public func handle(_ event: Event, context: LambdaContext) -> EventLoopFuture<Output> {
|
||||
let promise = context.eventLoop.makePromise(of: Output.self)
|
||||
promise.completeWithTask {
|
||||
@@ -51,25 +65,30 @@ extension LambdaHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
|
||||
extension LambdaHandler {
|
||||
public static func main() {
|
||||
_ = Lambda.run(handlerType: Self.self)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: - EventLoopLambdaHandler
|
||||
|
||||
/// Strongly typed, `EventLoopFuture` based processing protocol for a Lambda that takes a user defined `Event` and returns a user defined `Output` asynchronously.
|
||||
/// `EventLoopLambdaHandler` extends `ByteBufferLambdaHandler`, performing `ByteBuffer` -> `Event` decoding and `Output` -> `ByteBuffer` encoding.
|
||||
/// Strongly typed, `EventLoopFuture` based processing protocol for a Lambda that takes a user
|
||||
/// defined ``Event`` and returns a user defined ``Output`` asynchronously.
|
||||
///
|
||||
/// - note: To implement a Lambda, implement either `LambdaHandler` or the `EventLoopLambdaHandler` protocol.
|
||||
/// The `LambdaHandler` will offload the Lambda execution to a `DispatchQueue` making processing safer but slower
|
||||
/// The `EventLoopLambdaHandler` will execute the Lambda on the same `EventLoop` as the core runtime engine, making the processing faster but requires
|
||||
/// more care from the implementation to never block the `EventLoop`.
|
||||
/// ``EventLoopLambdaHandler`` extends ``ByteBufferLambdaHandler``, performing
|
||||
/// `ByteBuffer` -> ``Event`` decoding and ``Output`` -> `ByteBuffer` encoding.
|
||||
///
|
||||
/// - note: To implement a Lambda, implement either ``LambdaHandler`` or the
|
||||
/// ``EventLoopLambdaHandler`` protocol. The ``LambdaHandler`` will offload
|
||||
/// the Lambda execution to an async Task making processing safer but slower (due to
|
||||
/// fewer thread hops).
|
||||
/// The ``EventLoopLambdaHandler`` will execute the Lambda on the same `EventLoop`
|
||||
/// as the core runtime engine, making the processing faster but requires more care from the
|
||||
/// implementation to never block the `EventLoop`. Implement this protocol only in performance
|
||||
/// critical situations and implement ``LambdaHandler`` in all other circumstances.
|
||||
public protocol EventLoopLambdaHandler: ByteBufferLambdaHandler {
|
||||
/// The lambda functions input. In most cases this should be Codable. If your event originates from an
|
||||
/// AWS service, have a look at [AWSLambdaEvents](https://github.com/swift-server/swift-aws-lambda-events),
|
||||
/// which provides a number of commonly used AWS Event implementations.
|
||||
associatedtype Event
|
||||
/// The lambda functions output. Can be `Void`.
|
||||
associatedtype Output
|
||||
|
||||
/// The Lambda handling method
|
||||
@@ -135,9 +154,18 @@ extension EventLoopLambdaHandler where Output == Void {
|
||||
|
||||
/// An `EventLoopFuture` based processing protocol for a Lambda that takes a `ByteBuffer` and returns a `ByteBuffer?` asynchronously.
|
||||
///
|
||||
/// - note: This is a low level protocol designed to power the higher level `EventLoopLambdaHandler` and `LambdaHandler` based APIs.
|
||||
/// - note: This is a low level protocol designed to power the higher level ``EventLoopLambdaHandler`` and
|
||||
/// ``LambdaHandler`` based APIs.
|
||||
/// Most users are not expected to use this protocol.
|
||||
public protocol ByteBufferLambdaHandler {
|
||||
/// Create your Lambda handler for the runtime.
|
||||
///
|
||||
/// Use this to initialize all your resources that you want to cache between invocations. This could be database
|
||||
/// connections and HTTP clients for example. It is encouraged to use the given `EventLoop`'s conformance
|
||||
/// to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance, as it
|
||||
/// minimizes thread hopping.
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Self>
|
||||
|
||||
/// The Lambda handling method
|
||||
/// Concrete Lambda handlers implement this method to provide the Lambda functionality.
|
||||
///
|
||||
@@ -163,6 +191,20 @@ extension ByteBufferLambdaHandler {
|
||||
}
|
||||
}
|
||||
|
||||
extension ByteBufferLambdaHandler {
|
||||
/// Initializes and runs the lambda function.
|
||||
///
|
||||
/// If you precede your ``ByteBufferLambdaHandler`` conformer's declaration with the
|
||||
/// [@main](https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID626)
|
||||
/// attribute, the system calls the conformer's `main()` method to launch the lambda function.
|
||||
///
|
||||
/// The lambda runtime provides a default implementation of the method that manages the launch
|
||||
/// process.
|
||||
public static func main() {
|
||||
_ = Lambda.run(configuration: .init(), handlerType: Self.self)
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
enum CodecError: Error {
|
||||
case requestDecoding(Error)
|
||||
|
||||
@@ -34,14 +34,14 @@ extension Lambda {
|
||||
/// Run the user provided initializer. This *must* only be called once.
|
||||
///
|
||||
/// - Returns: An `EventLoopFuture<LambdaHandler>` fulfilled with the outcome of the initialization.
|
||||
func initialize(logger: Logger, factory: @escaping HandlerFactory) -> EventLoopFuture<Handler> {
|
||||
func initialize<Handler: ByteBufferLambdaHandler>(logger: Logger, handlerType: Handler.Type) -> EventLoopFuture<Handler> {
|
||||
logger.debug("initializing lambda")
|
||||
// 1. create the handler from the factory
|
||||
// 2. report initialization error if one occured
|
||||
let context = InitializationContext(logger: logger,
|
||||
eventLoop: self.eventLoop,
|
||||
allocator: self.allocator)
|
||||
return factory(context)
|
||||
return Handler.makeHandler(context: context)
|
||||
// Hopping back to "our" EventLoop is important in case the factory returns a future
|
||||
// that originated from a foreign EventLoop/EventLoopGroup.
|
||||
// This can happen if the factory uses a library (let's say a database client) that manages its own threads/loops
|
||||
@@ -56,7 +56,7 @@ extension Lambda {
|
||||
}
|
||||
}
|
||||
|
||||
func run(logger: Logger, handler: Handler) -> EventLoopFuture<Void> {
|
||||
func run<Handler: ByteBufferLambdaHandler>(logger: Logger, handler: Handler) -> EventLoopFuture<Void> {
|
||||
logger.debug("lambda invocation sequence starting")
|
||||
// 1. request invocation from lambda runtime engine
|
||||
self.isGettingNextInvocation = true
|
||||
|
||||
@@ -19,12 +19,11 @@ import NIOCore
|
||||
/// `LambdaRuntime` manages the Lambda process lifecycle.
|
||||
///
|
||||
/// - note: It is intended to be used within a single `EventLoop`. For this reason this class is not thread safe.
|
||||
public final class LambdaRuntime {
|
||||
public final class LambdaRuntime<Handler: ByteBufferLambdaHandler> {
|
||||
private let eventLoop: EventLoop
|
||||
private let shutdownPromise: EventLoopPromise<Int>
|
||||
private let logger: Logger
|
||||
private let configuration: Lambda.Configuration
|
||||
private let factory: Lambda.HandlerFactory
|
||||
|
||||
private var state = State.idle {
|
||||
willSet {
|
||||
@@ -38,17 +37,15 @@ public final class LambdaRuntime {
|
||||
/// - parameters:
|
||||
/// - eventLoop: An `EventLoop` to run the Lambda on.
|
||||
/// - logger: A `Logger` to log the Lambda events.
|
||||
/// - factory: A `LambdaHandlerFactory` to create the concrete Lambda handler.
|
||||
public convenience init(eventLoop: EventLoop, logger: Logger, factory: @escaping Lambda.HandlerFactory) {
|
||||
self.init(eventLoop: eventLoop, logger: logger, configuration: .init(), factory: factory)
|
||||
public convenience init(eventLoop: EventLoop, logger: Logger) {
|
||||
self.init(eventLoop: eventLoop, logger: logger, configuration: .init())
|
||||
}
|
||||
|
||||
init(eventLoop: EventLoop, logger: Logger, configuration: Lambda.Configuration, factory: @escaping Lambda.HandlerFactory) {
|
||||
init(eventLoop: EventLoop, logger: Logger, configuration: Lambda.Configuration) {
|
||||
self.eventLoop = eventLoop
|
||||
self.shutdownPromise = eventLoop.makePromise(of: Int.self)
|
||||
self.logger = logger
|
||||
self.configuration = configuration
|
||||
self.factory = factory
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -79,8 +76,8 @@ public final class LambdaRuntime {
|
||||
logger[metadataKey: "lifecycleId"] = .string(self.configuration.lifecycle.id)
|
||||
let runner = Lambda.Runner(eventLoop: self.eventLoop, configuration: self.configuration)
|
||||
|
||||
let startupFuture = runner.initialize(logger: logger, factory: self.factory)
|
||||
startupFuture.flatMap { handler -> EventLoopFuture<(ByteBufferLambdaHandler, Result<Int, Error>)> in
|
||||
let startupFuture = runner.initialize(logger: logger, handlerType: Handler.self)
|
||||
startupFuture.flatMap { handler -> EventLoopFuture<(Handler, Result<Int, Error>)> in
|
||||
// after the startup future has succeeded, we have a handler that we can use
|
||||
// to `run` the lambda.
|
||||
let finishedPromise = self.eventLoop.makePromise(of: Int.self)
|
||||
@@ -88,7 +85,7 @@ public final class LambdaRuntime {
|
||||
self.run(promise: finishedPromise)
|
||||
return finishedPromise.futureResult.mapResult { (handler, $0) }
|
||||
}
|
||||
.flatMap { (handler, runnerResult) -> EventLoopFuture<Int> in
|
||||
.flatMap { handler, runnerResult -> EventLoopFuture<Int> in
|
||||
// after the lambda finishPromise has succeeded or failed we need to
|
||||
// shutdown the handler
|
||||
let shutdownContext = Lambda.ShutdownContext(logger: logger, eventLoop: self.eventLoop)
|
||||
@@ -97,7 +94,7 @@ public final class LambdaRuntime {
|
||||
// the runner result
|
||||
logger.error("Error shutting down handler: \(error)")
|
||||
throw Lambda.RuntimeError.shutdownError(shutdownError: error, runnerResult: runnerResult)
|
||||
}.flatMapResult { (_) -> Result<Int, Error> in
|
||||
}.flatMapResult { _ -> Result<Int, Error> in
|
||||
// we had no error shutting down the lambda. let's return the runner's result
|
||||
runnerResult
|
||||
}
|
||||
@@ -173,7 +170,7 @@ public final class LambdaRuntime {
|
||||
private enum State {
|
||||
case idle
|
||||
case initializing
|
||||
case active(Lambda.Runner, Lambda.Handler)
|
||||
case active(Lambda.Runner, Handler)
|
||||
case shuttingdown
|
||||
case shutdown
|
||||
|
||||
|
||||
@@ -159,6 +159,10 @@ class LambdaHandlerTest: XCTestCase {
|
||||
typealias Event = String
|
||||
typealias Output = String
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Handler> {
|
||||
context.eventLoop.makeSucceededFuture(Handler())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
|
||||
context.eventLoop.makeSucceededFuture(event)
|
||||
}
|
||||
@@ -166,9 +170,7 @@ class LambdaHandlerTest: XCTestCase {
|
||||
|
||||
let maxTimes = Int.random(in: 1 ... 10)
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
|
||||
let result = Lambda.run(configuration: configuration, factory: { context in
|
||||
context.eventLoop.makeSucceededFuture(Handler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: Handler.self)
|
||||
assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
@@ -181,6 +183,10 @@ class LambdaHandlerTest: XCTestCase {
|
||||
typealias Event = String
|
||||
typealias Output = Void
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Handler> {
|
||||
context.eventLoop.makeSucceededFuture(Handler())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<Void> {
|
||||
context.eventLoop.makeSucceededFuture(())
|
||||
}
|
||||
@@ -188,9 +194,7 @@ class LambdaHandlerTest: XCTestCase {
|
||||
|
||||
let maxTimes = Int.random(in: 1 ... 10)
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
|
||||
let result = Lambda.run(configuration: configuration, factory: { context in
|
||||
context.eventLoop.makeSucceededFuture(Handler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: Handler.self)
|
||||
assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
@@ -203,6 +207,10 @@ class LambdaHandlerTest: XCTestCase {
|
||||
typealias Event = String
|
||||
typealias Output = String
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Handler> {
|
||||
context.eventLoop.makeSucceededFuture(Handler())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
|
||||
context.eventLoop.makeFailedFuture(TestError("boom"))
|
||||
}
|
||||
@@ -210,9 +218,7 @@ class LambdaHandlerTest: XCTestCase {
|
||||
|
||||
let maxTimes = Int.random(in: 1 ... 10)
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
|
||||
let result = Lambda.run(configuration: configuration, factory: { context in
|
||||
context.eventLoop.makeSucceededFuture(Handler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: Handler.self)
|
||||
assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
@@ -221,9 +227,21 @@ class LambdaHandlerTest: XCTestCase {
|
||||
XCTAssertNoThrow(try server.start().wait())
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
let result = Lambda.run(configuration: .init(), factory: { context in
|
||||
context.eventLoop.makeFailedFuture(TestError("kaboom"))
|
||||
})
|
||||
struct Handler: EventLoopLambdaHandler {
|
||||
typealias Event = String
|
||||
typealias Output = String
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Handler> {
|
||||
context.eventLoop.makeFailedFuture(TestError("kaboom"))
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
|
||||
XCTFail("Must never be called")
|
||||
return context.eventLoop.makeFailedFuture(TestError("boom"))
|
||||
}
|
||||
}
|
||||
|
||||
let result = Lambda.run(configuration: .init(), handlerType: Handler.self)
|
||||
assertLambdaRuntimeResult(result, shouldFailWithError: TestError("kaboom"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,27 +14,48 @@
|
||||
|
||||
import AWSLambdaRuntimeCore
|
||||
import NIOCore
|
||||
import XCTest
|
||||
|
||||
struct EchoHandler: EventLoopLambdaHandler {
|
||||
typealias Event = String
|
||||
typealias Output = String
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<EchoHandler> {
|
||||
context.eventLoop.makeSucceededFuture(EchoHandler())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
|
||||
context.eventLoop.makeSucceededFuture(event)
|
||||
}
|
||||
}
|
||||
|
||||
struct FailedHandler: EventLoopLambdaHandler {
|
||||
struct StartupError: Error {}
|
||||
|
||||
struct StartupErrorHandler: EventLoopLambdaHandler {
|
||||
typealias Event = String
|
||||
typealias Output = String
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<StartupErrorHandler> {
|
||||
context.eventLoop.makeFailedFuture(StartupError())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<String> {
|
||||
XCTFail("Must never be called")
|
||||
return context.eventLoop.makeSucceededFuture(event)
|
||||
}
|
||||
}
|
||||
|
||||
struct RuntimeError: Error {}
|
||||
|
||||
struct RuntimeErrorHandler: EventLoopLambdaHandler {
|
||||
typealias Event = String
|
||||
typealias Output = Void
|
||||
|
||||
private let reason: String
|
||||
|
||||
init(_ reason: String) {
|
||||
self.reason = reason
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<RuntimeErrorHandler> {
|
||||
context.eventLoop.makeSucceededFuture(RuntimeErrorHandler())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<Void> {
|
||||
context.eventLoop.makeFailedFuture(TestError(self.reason))
|
||||
context.eventLoop.makeFailedFuture(RuntimeError())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +40,11 @@ class LambdaRunnerTest: XCTestCase {
|
||||
return .failure(.internalServerError)
|
||||
}
|
||||
}
|
||||
XCTAssertNoThrow(try runLambda(behavior: Behavior(), handler: EchoHandler()))
|
||||
XCTAssertNoThrow(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self))
|
||||
}
|
||||
|
||||
func testFailure() {
|
||||
struct Behavior: LambdaServerBehavior {
|
||||
static let error = "boom"
|
||||
let requestId = UUID().uuidString
|
||||
func getInvocation() -> GetInvocationResult {
|
||||
.success((requestId: self.requestId, event: "hello"))
|
||||
@@ -58,7 +57,7 @@ class LambdaRunnerTest: XCTestCase {
|
||||
|
||||
func processError(requestId: String, error: ErrorResponse) -> Result<Void, ProcessErrorError> {
|
||||
XCTAssertEqual(self.requestId, requestId, "expecting requestId to match")
|
||||
XCTAssertEqual(Behavior.error, error.errorMessage, "expecting error to match")
|
||||
XCTAssertEqual(String(describing: RuntimeError()), error.errorMessage, "expecting error to match")
|
||||
return .success(())
|
||||
}
|
||||
|
||||
@@ -67,6 +66,6 @@ class LambdaRunnerTest: XCTestCase {
|
||||
return .failure(.internalServerError)
|
||||
}
|
||||
}
|
||||
XCTAssertNoThrow(try runLambda(behavior: Behavior(), handler: FailedHandler(Behavior.error)))
|
||||
XCTAssertNoThrow(try runLambda(behavior: Behavior(), handlerType: RuntimeErrorHandler.self))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,20 +24,20 @@ import XCTest
|
||||
class LambdaRuntimeClientTest: XCTestCase {
|
||||
func testSuccess() {
|
||||
let behavior = Behavior()
|
||||
XCTAssertNoThrow(try runLambda(behavior: behavior, handler: EchoHandler()))
|
||||
XCTAssertNoThrow(try runLambda(behavior: behavior, handlerType: EchoHandler.self))
|
||||
XCTAssertEqual(behavior.state, 6)
|
||||
}
|
||||
|
||||
func testFailure() {
|
||||
let behavior = Behavior()
|
||||
XCTAssertNoThrow(try runLambda(behavior: behavior, handler: FailedHandler("boom")))
|
||||
XCTAssertNoThrow(try runLambda(behavior: behavior, handlerType: RuntimeErrorHandler.self))
|
||||
XCTAssertEqual(behavior.state, 10)
|
||||
}
|
||||
|
||||
func testBootstrapFailure() {
|
||||
func testStartupFailure() {
|
||||
let behavior = Behavior()
|
||||
XCTAssertThrowsError(try runLambda(behavior: behavior, factory: { $0.eventLoop.makeFailedFuture(TestError("boom")) })) { error in
|
||||
XCTAssertEqual(error as? TestError, TestError("boom"))
|
||||
XCTAssertThrowsError(try runLambda(behavior: behavior, handlerType: StartupErrorHandler.self)) {
|
||||
XCTAssert($0 is StartupError)
|
||||
}
|
||||
XCTAssertEqual(behavior.state, 1)
|
||||
}
|
||||
@@ -63,8 +63,8 @@ class LambdaRuntimeClientTest: XCTestCase {
|
||||
return .failure(.internalServerError)
|
||||
}
|
||||
}
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: EchoHandler())) { error in
|
||||
XCTAssertEqual(error as? Lambda.RuntimeError, .badStatusCode(.internalServerError))
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) {
|
||||
XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ class LambdaRuntimeClientTest: XCTestCase {
|
||||
return .failure(.internalServerError)
|
||||
}
|
||||
}
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: EchoHandler())) { error in
|
||||
XCTAssertEqual(error as? Lambda.RuntimeError, .noBody)
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) {
|
||||
XCTAssertEqual($0 as? Lambda.RuntimeError, .noBody)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,8 +116,8 @@ class LambdaRuntimeClientTest: XCTestCase {
|
||||
return .failure(.internalServerError)
|
||||
}
|
||||
}
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: EchoHandler())) { error in
|
||||
XCTAssertEqual(error as? Lambda.RuntimeError, .invocationMissingHeader(AmazonHeaders.requestID))
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) {
|
||||
XCTAssertEqual($0 as? Lambda.RuntimeError, .invocationMissingHeader(AmazonHeaders.requestID))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,8 +141,8 @@ class LambdaRuntimeClientTest: XCTestCase {
|
||||
return .failure(.internalServerError)
|
||||
}
|
||||
}
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: EchoHandler())) { error in
|
||||
XCTAssertEqual(error as? Lambda.RuntimeError, .badStatusCode(.internalServerError))
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) {
|
||||
XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,8 @@ class LambdaRuntimeClientTest: XCTestCase {
|
||||
return .failure(.internalServerError)
|
||||
}
|
||||
}
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: FailedHandler("boom"))) { error in
|
||||
XCTAssertEqual(error as? Lambda.RuntimeError, .badStatusCode(.internalServerError))
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: RuntimeErrorHandler.self)) {
|
||||
XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,8 +192,8 @@ class LambdaRuntimeClientTest: XCTestCase {
|
||||
.failure(.internalServerError)
|
||||
}
|
||||
}
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), factory: { $0.eventLoop.makeFailedFuture(TestError("boom")) })) { error in
|
||||
XCTAssertEqual(error as? TestError, TestError("boom"))
|
||||
XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: StartupErrorHandler.self)) {
|
||||
XCTAssert($0 is StartupError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,37 +29,16 @@ class LambdaRuntimeTest: XCTestCase {
|
||||
|
||||
let eventLoop = eventLoopGroup.next()
|
||||
let logger = Logger(label: "TestLogger")
|
||||
let testError = TestError("kaboom")
|
||||
let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, factory: {
|
||||
$0.eventLoop.makeFailedFuture(testError)
|
||||
})
|
||||
let runtime = LambdaRuntime<StartupErrorHandler>(eventLoop: eventLoop, logger: logger)
|
||||
|
||||
// eventLoop.submit in this case returns an EventLoopFuture<EventLoopFuture<ByteBufferHandler>>
|
||||
// which is why we need `wait().wait()`
|
||||
XCTAssertThrowsError(try eventLoop.flatSubmit { runtime.start() }.wait()) { error in
|
||||
XCTAssertEqual(testError, error as? TestError)
|
||||
XCTAssertThrowsError(try eventLoop.flatSubmit { runtime.start() }.wait()) {
|
||||
XCTAssert($0 is StartupError)
|
||||
}
|
||||
|
||||
XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { error in
|
||||
XCTAssertEqual(testError, error as? TestError)
|
||||
}
|
||||
}
|
||||
|
||||
struct CallbackLambdaHandler: ByteBufferLambdaHandler {
|
||||
let handler: (LambdaContext, ByteBuffer) -> (EventLoopFuture<ByteBuffer?>)
|
||||
let shutdown: (Lambda.ShutdownContext) -> EventLoopFuture<Void>
|
||||
|
||||
init(_ handler: @escaping (LambdaContext, ByteBuffer) -> (EventLoopFuture<ByteBuffer?>), shutdown: @escaping (Lambda.ShutdownContext) -> EventLoopFuture<Void>) {
|
||||
self.handler = handler
|
||||
self.shutdown = shutdown
|
||||
}
|
||||
|
||||
func handle(_ event: ByteBuffer, context: LambdaContext) -> EventLoopFuture<ByteBuffer?> {
|
||||
self.handler(context, event)
|
||||
}
|
||||
|
||||
func shutdown(context: Lambda.ShutdownContext) -> EventLoopFuture<Void> {
|
||||
self.shutdown(context)
|
||||
XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) {
|
||||
XCTAssert($0 is StartupError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,23 +49,14 @@ class LambdaRuntimeTest: XCTestCase {
|
||||
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
|
||||
|
||||
var count = 0
|
||||
let handler = CallbackLambdaHandler({ XCTFail("Should not be reached"); return $0.eventLoop.makeSucceededFuture($1) }) { context in
|
||||
count += 1
|
||||
return context.eventLoop.makeSucceededFuture(())
|
||||
}
|
||||
|
||||
let eventLoop = eventLoopGroup.next()
|
||||
let logger = Logger(label: "TestLogger")
|
||||
let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(handler)
|
||||
})
|
||||
let runtime = LambdaRuntime<EchoHandler>(eventLoop: eventLoop, logger: logger)
|
||||
|
||||
XCTAssertNoThrow(_ = try eventLoop.flatSubmit { runtime.start() }.wait())
|
||||
XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { error in
|
||||
XCTAssertEqual(.badStatusCode(HTTPResponseStatus.internalServerError), error as? Lambda.RuntimeError)
|
||||
XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) {
|
||||
XCTAssertEqual(.badStatusCode(HTTPResponseStatus.internalServerError), $0 as? Lambda.RuntimeError)
|
||||
}
|
||||
XCTAssertEqual(count, 1)
|
||||
}
|
||||
|
||||
func testLambdaResultIfShutsdownIsUnclean() {
|
||||
@@ -96,28 +66,38 @@ class LambdaRuntimeTest: XCTestCase {
|
||||
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
|
||||
|
||||
var count = 0
|
||||
let handler = CallbackLambdaHandler({ XCTFail("Should not be reached"); return $0.eventLoop.makeSucceededFuture($1) }) { context in
|
||||
count += 1
|
||||
return context.eventLoop.makeFailedFuture(TestError("kaboom"))
|
||||
struct ShutdownError: Error {}
|
||||
|
||||
struct ShutdownErrorHandler: EventLoopLambdaHandler {
|
||||
typealias Event = String
|
||||
typealias Output = Void
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<ShutdownErrorHandler> {
|
||||
context.eventLoop.makeSucceededFuture(ShutdownErrorHandler())
|
||||
}
|
||||
|
||||
func handle(_ event: String, context: LambdaContext) -> EventLoopFuture<Void> {
|
||||
context.eventLoop.makeSucceededVoidFuture()
|
||||
}
|
||||
|
||||
func shutdown(context: Lambda.ShutdownContext) -> EventLoopFuture<Void> {
|
||||
context.eventLoop.makeFailedFuture(ShutdownError())
|
||||
}
|
||||
}
|
||||
|
||||
let eventLoop = eventLoopGroup.next()
|
||||
let logger = Logger(label: "TestLogger")
|
||||
let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(handler)
|
||||
})
|
||||
let runtime = LambdaRuntime<ShutdownErrorHandler>(eventLoop: eventLoop, logger: logger)
|
||||
|
||||
XCTAssertNoThrow(_ = try eventLoop.flatSubmit { runtime.start() }.wait())
|
||||
XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { error in
|
||||
XCTAssertNoThrow(try eventLoop.flatSubmit { runtime.start() }.wait())
|
||||
XCTAssertThrowsError(try runtime.shutdownFuture.wait()) { error in
|
||||
guard case Lambda.RuntimeError.shutdownError(let shutdownError, .failure(let runtimeError)) = error else {
|
||||
XCTFail("Unexpected error"); return
|
||||
XCTFail("Unexpected error: \(error)"); return
|
||||
}
|
||||
|
||||
XCTAssertEqual(shutdownError as? TestError, TestError("kaboom"))
|
||||
XCTAssert(shutdownError is ShutdownError)
|
||||
XCTAssertEqual(runtimeError as? Lambda.RuntimeError, .badStatusCode(.internalServerError))
|
||||
}
|
||||
XCTAssertEqual(count, 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,22 +26,18 @@ class LambdaTest: XCTestCase {
|
||||
|
||||
let maxTimes = Int.random(in: 10 ... 20)
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
|
||||
let result = Lambda.run(configuration: configuration, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(EchoHandler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self)
|
||||
assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
func testFailure() {
|
||||
let server = MockLambdaServer(behavior: Behavior(result: .failure(TestError("boom"))))
|
||||
let server = MockLambdaServer(behavior: Behavior(result: .failure(RuntimeError())))
|
||||
XCTAssertNoThrow(try server.start().wait())
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
let maxTimes = Int.random(in: 10 ... 20)
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
|
||||
let result = Lambda.run(configuration: configuration, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(FailedHandler("boom"))
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: RuntimeErrorHandler.self)
|
||||
assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
@@ -50,8 +46,8 @@ class LambdaTest: XCTestCase {
|
||||
XCTAssertNoThrow(try server.start().wait())
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
let result = Lambda.run(factory: { $0.eventLoop.makeFailedFuture(TestError("kaboom")) })
|
||||
assertLambdaRuntimeResult(result, shouldFailWithError: TestError("kaboom"))
|
||||
let result = Lambda.run(configuration: .init(), handlerType: StartupErrorHandler.self)
|
||||
assertLambdaRuntimeResult(result, shouldFailWithError: StartupError())
|
||||
}
|
||||
|
||||
func testBootstrapFailureAndReportErrorFailure() {
|
||||
@@ -80,8 +76,8 @@ class LambdaTest: XCTestCase {
|
||||
XCTAssertNoThrow(try server.start().wait())
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
let result = Lambda.run(factory: { $0.eventLoop.makeFailedFuture(TestError("kaboom")) })
|
||||
assertLambdaRuntimeResult(result, shouldFailWithError: TestError("kaboom"))
|
||||
let result = Lambda.run(configuration: .init(), handlerType: StartupErrorHandler.self)
|
||||
assertLambdaRuntimeResult(result, shouldFailWithError: StartupError())
|
||||
}
|
||||
|
||||
func testStartStopInDebugMode() {
|
||||
@@ -99,7 +95,7 @@ class LambdaTest: XCTestCase {
|
||||
usleep(100_000)
|
||||
kill(getpid(), signal.rawValue)
|
||||
}
|
||||
let result = Lambda.run(configuration: configuration, factory: { $0.eventLoop.makeSucceededFuture(EchoHandler()) })
|
||||
let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self)
|
||||
|
||||
switch result {
|
||||
case .success(let invocationCount):
|
||||
@@ -118,9 +114,7 @@ class LambdaTest: XCTestCase {
|
||||
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1),
|
||||
runtimeEngine: .init(requestTimeout: .milliseconds(timeout)))
|
||||
let result = Lambda.run(configuration: configuration, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(EchoHandler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self)
|
||||
assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.upstreamError("timeout"))
|
||||
}
|
||||
|
||||
@@ -130,9 +124,7 @@ class LambdaTest: XCTestCase {
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1))
|
||||
let result = Lambda.run(configuration: configuration, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(EchoHandler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self)
|
||||
assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.upstreamError("connectionResetByPeer"))
|
||||
}
|
||||
|
||||
@@ -143,9 +135,7 @@ class LambdaTest: XCTestCase {
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1))
|
||||
let result = Lambda.run(configuration: configuration, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(EchoHandler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self)
|
||||
assertLambdaRuntimeResult(result, shoudHaveRun: 1)
|
||||
}
|
||||
|
||||
@@ -156,9 +146,7 @@ class LambdaTest: XCTestCase {
|
||||
|
||||
let maxTimes = 10
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
|
||||
let result = Lambda.run(configuration: configuration, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(EchoHandler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self)
|
||||
assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
@@ -169,9 +157,7 @@ class LambdaTest: XCTestCase {
|
||||
|
||||
let maxTimes = 10
|
||||
let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes))
|
||||
let result = Lambda.run(configuration: configuration, factory: {
|
||||
$0.eventLoop.makeSucceededFuture(EchoHandler())
|
||||
})
|
||||
let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self)
|
||||
assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
@@ -199,9 +185,7 @@ class LambdaTest: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
let result = Lambda.run(configuration: .init(), factory: {
|
||||
$0.eventLoop.makeSucceededFuture(EchoHandler())
|
||||
})
|
||||
let result = Lambda.run(configuration: .init(), handlerType: EchoHandler.self)
|
||||
assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.badStatusCode(.internalServerError))
|
||||
}
|
||||
|
||||
@@ -271,9 +255,9 @@ class LambdaTest: XCTestCase {
|
||||
private struct Behavior: LambdaServerBehavior {
|
||||
let requestId: String
|
||||
let event: String
|
||||
let result: Result<String?, TestError>
|
||||
let result: Result<String?, RuntimeError>
|
||||
|
||||
init(requestId: String = UUID().uuidString, event: String = "hello", result: Result<String?, TestError> = .success("hello")) {
|
||||
init(requestId: String = UUID().uuidString, event: String = "hello", result: Result<String?, RuntimeError> = .success("hello")) {
|
||||
self.requestId = requestId
|
||||
self.event = event
|
||||
self.result = result
|
||||
@@ -302,7 +286,7 @@ private struct Behavior: LambdaServerBehavior {
|
||||
XCTFail("unexpected to succeed, but failed with: \(error)")
|
||||
return .failure(.internalServerError)
|
||||
case .failure(let expected):
|
||||
XCTAssertEqual(expected.description, error.errorMessage, "expecting error to match")
|
||||
XCTAssertEqual(String(describing: expected), error.errorMessage, "expecting error to match")
|
||||
return .success(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,7 @@ import NIOCore
|
||||
import NIOPosix
|
||||
import XCTest
|
||||
|
||||
func runLambda(behavior: LambdaServerBehavior, handler: Lambda.Handler) throws {
|
||||
try runLambda(behavior: behavior, factory: { $0.eventLoop.makeSucceededFuture(handler) })
|
||||
}
|
||||
|
||||
func runLambda(behavior: LambdaServerBehavior, factory: @escaping Lambda.HandlerFactory) throws {
|
||||
func runLambda<Handler: ByteBufferLambdaHandler>(behavior: LambdaServerBehavior, handlerType: Handler.Type) throws {
|
||||
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
|
||||
let logger = Logger(label: "TestLogger")
|
||||
@@ -30,7 +26,7 @@ func runLambda(behavior: LambdaServerBehavior, factory: @escaping Lambda.Handler
|
||||
let runner = Lambda.Runner(eventLoop: eventLoopGroup.next(), configuration: configuration)
|
||||
let server = try MockLambdaServer(behavior: behavior).start().wait()
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
try runner.initialize(logger: logger, factory: factory).flatMap { handler in
|
||||
try runner.initialize(logger: logger, handlerType: handlerType).flatMap { handler in
|
||||
runner.run(logger: logger, handler: handler)
|
||||
}.wait()
|
||||
}
|
||||
|
||||
@@ -41,7 +41,11 @@ class CodableLambdaTest: XCTestCase {
|
||||
typealias Event = Request
|
||||
typealias Output = Void
|
||||
|
||||
let expected: Request
|
||||
var expected: Request?
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Handler> {
|
||||
context.eventLoop.makeSucceededFuture(Handler())
|
||||
}
|
||||
|
||||
func handle(_ event: Request, context: LambdaContext) -> EventLoopFuture<Void> {
|
||||
XCTAssertEqual(event, self.expected)
|
||||
@@ -66,7 +70,11 @@ class CodableLambdaTest: XCTestCase {
|
||||
typealias Event = Request
|
||||
typealias Output = Response
|
||||
|
||||
let expected: Request
|
||||
var expected: Request?
|
||||
|
||||
static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture<Handler> {
|
||||
context.eventLoop.makeSucceededFuture(Handler())
|
||||
}
|
||||
|
||||
func handle(_ event: Request, context: LambdaContext) -> EventLoopFuture<Response> {
|
||||
XCTAssertEqual(event, self.expected)
|
||||
|
||||
Reference in New Issue
Block a user