Skip to content

Commit 6b8acb3

Browse files
committed
add a testing harness to ease testing of lambdas
motivation: make testing lambda easy changes: * add a AWSLambdaTesting module * add helper methods for testing different types of Lambda handlers / closures
1 parent dede451 commit 6b8acb3

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

Package.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ let package = Package(
88
.macOS(.v10_13),
99
],
1010
products: [
11+
// core library
1112
.library(name: "AWSLambdaRuntime", targets: ["AWSLambdaRuntime"]),
13+
// common AWS events
1214
.library(name: "AWSLambdaEvents", targets: ["AWSLambdaEvents"]),
15+
// for testing only
16+
.library(name: "AWSLambdaTesting", targets: ["AWSLambdaTesting"]),
1317
],
1418
dependencies: [
1519
.package(url: "https://github.com/apple/swift-nio.git", from: "2.8.0"),
@@ -26,6 +30,12 @@ let package = Package(
2630
.testTarget(name: "AWSLambdaRuntimeTests", dependencies: ["AWSLambdaRuntime"]),
2731
.target(name: "AWSLambdaEvents", dependencies: []),
2832
.testTarget(name: "AWSLambdaEventsTests", dependencies: ["AWSLambdaEvents"]),
33+
// testing helper
34+
.target(name: "AWSLambdaTesting", dependencies: [
35+
"AWSLambdaRuntime",
36+
.product(name: "NIOHTTP1", package: "swift-nio"),
37+
]),
38+
.testTarget(name: "AWSLambdaTestingTests", dependencies: ["AWSLambdaTesting"]),
2939
// samples
3040
.target(name: "StringSample", dependencies: ["AWSLambdaRuntime"]),
3141
.target(name: "CodableSample", dependencies: ["AWSLambdaRuntime"]),
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
// @testable for access of internal functions - this would only work for testing by design
16+
@testable import AWSLambdaRuntime
17+
import Dispatch
18+
import Logging
19+
import NIO
20+
21+
extension Lambda {
22+
public static func test(_ closure: @escaping StringLambdaClosure,
23+
with payload: String,
24+
_ body: @escaping (Result<String, Error>) -> Void) {
25+
Self.test(StringLambdaClosureWrapper(closure), with: payload, body)
26+
}
27+
28+
public static func test(_ closure: @escaping StringVoidLambdaClosure,
29+
with payload: String,
30+
_ body: @escaping (Result<Void, Error>) -> Void) {
31+
Self.test(StringVoidLambdaClosureWrapper(closure), with: payload, body)
32+
}
33+
34+
public static func test<In: Decodable, Out: Encodable>(_ closure: @escaping CodableLambdaClosure<In, Out>,
35+
with payload: In,
36+
_ body: @escaping (Result<Out, Error>) -> Void) {
37+
Self.test(CodableLambdaClosureWrapper(closure), with: payload, body)
38+
}
39+
40+
public static func test<In: Decodable>(_ closure: @escaping CodableVoidLambdaClosure<In>,
41+
with payload: In,
42+
_ body: @escaping (Result<Void, Error>) -> Void) {
43+
Self.test(CodableVoidLambdaClosureWrapper(closure), with: payload, body)
44+
}
45+
46+
public static func test<In, Out, Handler: EventLoopLambdaHandler>(_ handler: Handler,
47+
with payload: In,
48+
_ body: @escaping (Result<Out, Error>) -> Void) where Handler.In == In, Handler.Out == Out {
49+
let logger = Logger(label: "test")
50+
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
51+
let context = Context(requestId: "\(DispatchTime.now().uptimeNanoseconds)",
52+
traceId: "Root=\(DispatchTime.now().uptimeNanoseconds);Parent=\(DispatchTime.now().uptimeNanoseconds);Sampled=1",
53+
invokedFunctionArn: "arn:aws:lambda:us-west-1:\(DispatchTime.now().uptimeNanoseconds):function:custom-runtime",
54+
deadline: .now() + 5,
55+
logger: logger,
56+
eventLoop: eventLoopGroup.next())
57+
handler.handle(context: context, payload: payload).whenComplete { result in
58+
body(result)
59+
}
60+
}
61+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import AWSLambdaRuntime
16+
import AWSLambdaTesting
17+
import XCTest
18+
19+
class LambdaTestingTests: XCTestCase {
20+
func testCodableClosure() {
21+
struct Request: Codable {
22+
let name: String
23+
}
24+
25+
struct Response: Codable {
26+
let message: String
27+
}
28+
29+
let myLambda = { (_: Lambda.Context, request: Request, callback: (Result<Response, Error>) -> Void) in
30+
callback(.success(Response(message: "echo" + request.name)))
31+
}
32+
33+
let request = Request(name: UUID().uuidString)
34+
Lambda.test(myLambda, with: request) { result in
35+
switch result {
36+
case .failure(let error):
37+
XCTFail("expected to succeed but failed with \(error)")
38+
case .success(let response):
39+
XCTAssertEqual(response.message, "echo" + request.name)
40+
}
41+
}
42+
}
43+
44+
func testCodableVoidClosure() {
45+
struct Request: Codable {
46+
let name: String
47+
}
48+
49+
let myLambda = { (_: Lambda.Context, _: Request, callback: (Result<Void, Error>) -> Void) in
50+
callback(.success(()))
51+
}
52+
53+
let request = Request(name: UUID().uuidString)
54+
Lambda.test(myLambda, with: request) { result in
55+
switch result {
56+
case .failure(let error):
57+
XCTFail("expected to succeed but failed with \(error)")
58+
case .success:
59+
break
60+
}
61+
}
62+
}
63+
64+
func testLambdaHandler() {
65+
struct Request: Codable {
66+
let name: String
67+
}
68+
69+
struct Response: Codable {
70+
let message: String
71+
}
72+
73+
struct MyLambda: LambdaHandler {
74+
typealias In = Request
75+
typealias Out = Response
76+
77+
func handle(context: Lambda.Context, payload: In, callback: @escaping (Result<Out, Error>) -> Void) {
78+
callback(.success(Response(message: "echo" + payload.name)))
79+
}
80+
}
81+
82+
let request = Request(name: UUID().uuidString)
83+
Lambda.test(MyLambda(), with: request) { result in
84+
switch result {
85+
case .failure(let error):
86+
XCTFail("expected to succeed but failed with \(error)")
87+
case .success(let response):
88+
XCTAssertEqual(response.message, "echo" + request.name)
89+
}
90+
}
91+
}
92+
93+
func testFailure() {
94+
struct MyError: Error {}
95+
96+
struct MyLambda: LambdaHandler {
97+
typealias In = String
98+
typealias Out = Void
99+
100+
func handle(context: Lambda.Context, payload: In, callback: @escaping (Result<Out, Error>) -> Void) {
101+
callback(.failure(MyError()))
102+
}
103+
}
104+
105+
Lambda.test(MyLambda(), with: UUID().uuidString) { result in
106+
switch result {
107+
case .failure(let error):
108+
XCTAssert(error is MyError)
109+
case .success:
110+
XCTFail("expected to fail but succeeded")
111+
}
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)