Skip to content

Commit d1bb76f

Browse files
authored
Add Async/await tests for Functions (#8959)
1 parent f023aa5 commit d1bb76f

File tree

5 files changed

+247
-15
lines changed

5 files changed

+247
-15
lines changed

.github/workflows/combine.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ on:
2020
# Combine sources
2121
- 'FirebaseCombineSwift/**'
2222

23-
# Podspec
23+
# Podspecs
2424
- 'FirebaseCombineSwift.podspec'
25+
- 'FirebaseFunctionsTestingSupport.podspec'
2526

2627
# This workflow
2728
- '.github/workflows/combine.yml'

.github/workflows/functions.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
runs-on: macos-11
2626
strategy:
2727
matrix:
28-
target: [ios, tvos, macos]
28+
target: [ios, tvos, macos, watchos]
2929
steps:
3030
- uses: actions/checkout@v2
3131
- name: Setup Bundler

FirebaseFunctions.podspec

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,19 @@ Cloud Functions for Firebase.
5454
s.test_spec 'integration' do |int_tests|
5555
int_tests.scheme = { :code_coverage => true }
5656
int_tests.source_files = 'FirebaseFunctions/Tests/Integration/*.[mh]',
57-
'FirebaseFunctions/Tests/Integration/*.plist',
5857
'SharedTestUtilities/FIRAuthInteropFake*',
5958
'SharedTestUtilities/FIRMessagingInteropFake*'
6059
end
60+
61+
# Uncomment to use pod gen to run the Swift Integration tests. This can't be
62+
# committed because of the dependency on the unpublished FirebaseFunctionsTestingSupport.
63+
# Alternatively, use Swift Package Manager to run the swift integration tests locally.
64+
#
65+
# s.test_spec 'swift-integration' do |swift_int|
66+
# swift_int.platforms = {:ios => '15.0', :osx => '12.0', :tvos => '15.0', :watchos => '8.0'}
67+
# swift_int.scheme = { :code_coverage => true }
68+
# swift_int.dependency 'FirebaseFunctionsTestingSupport'
69+
# swift_int.source_files = 'FirebaseFunctions/Tests/SwiftIntegration/*',
70+
# 'FirebaseTestingSupport/Functions/Sources/*'
71+
# end
6172
end

FirebaseFunctions/Tests/SwiftIntegration/IntegrationTests.swift

Lines changed: 230 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,35 @@ class IntegrationTests: XCTestCase {
6565
XCTAssert(false, "Failed to unwrap the function result: \(error)")
6666
}
6767
}
68-
waitForExpectations(timeout: 1)
68+
waitForExpectations(timeout: 5)
6969
}
7070

71+
#if compiler(>=5.5) && canImport(_Concurrency)
72+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
73+
func testDataAsync() async throws {
74+
let input = [
75+
"bool": true,
76+
"int": 2 as Int32,
77+
"long": 9_876_543_210,
78+
"string": "four",
79+
"array": [5 as Int32, 6 as Int32],
80+
"null": nil,
81+
] as [String: Any?]
82+
83+
let function = functions.httpsCallable("dataTest")
84+
XCTAssertNotNil(function)
85+
86+
let result = try await function.call(input)
87+
let data = try XCTUnwrap(result.data as? [String: Any])
88+
let message = try XCTUnwrap(data["message"] as? String)
89+
let long = try XCTUnwrap(data["long"] as? Int64)
90+
let code = try XCTUnwrap(data["code"] as? Int32)
91+
XCTAssertEqual(message, "stub response")
92+
XCTAssertEqual(long, 420)
93+
XCTAssertEqual(code, 42)
94+
}
95+
#endif
96+
7197
func testScalar() {
7298
let expectation = expectation(description: #function)
7399
let function = functions.httpsCallable("scalarTest")
@@ -82,11 +108,23 @@ class IntegrationTests: XCTestCase {
82108
XCTAssert(false, "Failed to unwrap the function result: \(error)")
83109
}
84110
}
85-
waitForExpectations(timeout: 1)
111+
waitForExpectations(timeout: 5)
86112
}
87113

114+
#if compiler(>=5.5) && canImport(_Concurrency)
115+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
116+
func testScalarAsync() async throws {
117+
let function = functions.httpsCallable("scalarTest")
118+
XCTAssertNotNil(function)
119+
120+
let result = try await function.call(17 as Int16)
121+
let data = try XCTUnwrap(result.data as? Int)
122+
XCTAssertEqual(data, 76)
123+
}
124+
#endif
125+
88126
func testToken() {
89-
// Recreate _functions with a token.
127+
// Recreate functions with a token.
90128
let functions = FunctionsFake(
91129
projectID: "functions-integration-test",
92130
region: "us-central1",
@@ -108,9 +146,30 @@ class IntegrationTests: XCTestCase {
108146
XCTAssert(false, "Failed to unwrap the function result: \(error)")
109147
}
110148
}
111-
waitForExpectations(timeout: 1)
149+
waitForExpectations(timeout: 5)
112150
}
113151

152+
#if compiler(>=5.5) && canImport(_Concurrency)
153+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
154+
func testTokenAsync() async throws {
155+
// Recreate functions with a token.
156+
let functions = FunctionsFake(
157+
projectID: "functions-integration-test",
158+
region: "us-central1",
159+
customDomain: nil,
160+
withToken: "token"
161+
)
162+
functions.useLocalhost()
163+
164+
let function = functions.httpsCallable("FCMTokenTest")
165+
XCTAssertNotNil(function)
166+
167+
let result = try await function.call([:])
168+
let data = try XCTUnwrap(result.data) as? [String: Int]
169+
XCTAssertEqual(data, [:])
170+
}
171+
#endif
172+
114173
func testFCMToken() {
115174
let expectation = expectation(description: #function)
116175
let function = functions.httpsCallable("FCMTokenTest")
@@ -125,9 +184,21 @@ class IntegrationTests: XCTestCase {
125184
XCTAssert(false, "Failed to unwrap the function result: \(error)")
126185
}
127186
}
128-
waitForExpectations(timeout: 1)
187+
waitForExpectations(timeout: 5)
129188
}
130189

190+
#if compiler(>=5.5) && canImport(_Concurrency)
191+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
192+
func testFCMTokenAsync() async throws {
193+
let function = functions.httpsCallable("FCMTokenTest")
194+
XCTAssertNotNil(function)
195+
196+
let result = try await function.call([:])
197+
let data = try XCTUnwrap(result.data) as? [String: Int]
198+
XCTAssertEqual(data, [:])
199+
}
200+
#endif
201+
131202
func testNull() {
132203
let expectation = expectation(description: #function)
133204
let function = functions.httpsCallable("nullTest")
@@ -142,9 +213,51 @@ class IntegrationTests: XCTestCase {
142213
XCTAssert(false, "Failed to unwrap the function result: \(error)")
143214
}
144215
}
145-
waitForExpectations(timeout: 1)
216+
waitForExpectations(timeout: 5)
217+
}
218+
219+
#if compiler(>=5.5) && canImport(_Concurrency)
220+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
221+
func testNullAsync() async throws {
222+
let function = functions.httpsCallable("nullTest")
223+
XCTAssertNotNil(function)
224+
225+
let result = try await function.call(nil)
226+
let data = try XCTUnwrap(result.data) as? NSNull
227+
XCTAssertEqual(data, NSNull())
228+
}
229+
#endif
230+
231+
// No parameters to call should be the same as passing nil.
232+
func testParameterless() {
233+
let expectation = expectation(description: #function)
234+
let function = functions.httpsCallable("nullTest")
235+
XCTAssertNotNil(function)
236+
function.call { result, error in
237+
do {
238+
XCTAssertNil(error)
239+
let data = try XCTUnwrap(result?.data) as? NSNull
240+
XCTAssertEqual(data, NSNull())
241+
expectation.fulfill()
242+
} catch {
243+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
244+
}
245+
}
246+
waitForExpectations(timeout: 5)
146247
}
147248

249+
#if compiler(>=5.5) && canImport(_Concurrency)
250+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
251+
func testParameterlessAsync() async throws {
252+
let function = functions.httpsCallable("nullTest")
253+
XCTAssertNotNil(function)
254+
255+
let result = try await function.call()
256+
let data = try XCTUnwrap(result.data) as? NSNull
257+
XCTAssertEqual(data, NSNull())
258+
}
259+
#endif
260+
148261
func testMissingResult() {
149262
let expectation = expectation(description: #function)
150263
let function = functions.httpsCallable("missingResultTest")
@@ -161,9 +274,26 @@ class IntegrationTests: XCTestCase {
161274
}
162275
}
163276
XCTAssert(true)
164-
waitForExpectations(timeout: 1)
277+
waitForExpectations(timeout: 5)
165278
}
166279

280+
#if compiler(>=5.5) && canImport(_Concurrency)
281+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
282+
func testMissingResultAsync() async throws {
283+
let function = functions.httpsCallable("missingResultTest")
284+
XCTAssertNotNil(function)
285+
do {
286+
_ = try await function.call(nil)
287+
} catch {
288+
let error = try XCTUnwrap(error) as NSError
289+
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
290+
XCTAssertEqual("Response is missing data field.", error.localizedDescription)
291+
return
292+
}
293+
XCTAssertFalse(true, "Failed to throw error for missing result")
294+
}
295+
#endif
296+
167297
func testUnhandledError() {
168298
let expectation = expectation(description: #function)
169299
let function = functions.httpsCallable("unhandledErrorTest")
@@ -180,9 +310,26 @@ class IntegrationTests: XCTestCase {
180310
}
181311
}
182312
XCTAssert(true)
183-
waitForExpectations(timeout: 1)
313+
waitForExpectations(timeout: 5)
184314
}
185315

316+
#if compiler(>=5.5) && canImport(_Concurrency)
317+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
318+
func testUnhandledErrorAsync() async throws {
319+
let function = functions.httpsCallable("unhandledErrorTest")
320+
XCTAssertNotNil(function)
321+
do {
322+
_ = try await function.call([])
323+
} catch {
324+
let error = try XCTUnwrap(error) as NSError
325+
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
326+
XCTAssertEqual("INTERNAL", error.localizedDescription)
327+
return
328+
}
329+
XCTAssertFalse(true, "Failed to throw error for missing result")
330+
}
331+
#endif
332+
186333
func testUnknownError() {
187334
let expectation = expectation(description: #function)
188335
let function = functions.httpsCallable("unknownErrorTest")
@@ -199,9 +346,26 @@ class IntegrationTests: XCTestCase {
199346
}
200347
}
201348
XCTAssert(true)
202-
waitForExpectations(timeout: 1)
349+
waitForExpectations(timeout: 5)
203350
}
204351

352+
#if compiler(>=5.5) && canImport(_Concurrency)
353+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
354+
func testUnknownErrorAsync() async throws {
355+
let function = functions.httpsCallable("unknownErrorTest")
356+
XCTAssertNotNil(function)
357+
do {
358+
_ = try await function.call([])
359+
} catch {
360+
let error = try XCTUnwrap(error) as NSError
361+
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
362+
XCTAssertEqual("INTERNAL", error.localizedDescription)
363+
return
364+
}
365+
XCTAssertFalse(true, "Failed to throw error for missing result")
366+
}
367+
#endif
368+
205369
func testExplicitError() {
206370
let expectation = expectation(description: #function)
207371
let function = functions.httpsCallable("explicitErrorTest")
@@ -220,9 +384,28 @@ class IntegrationTests: XCTestCase {
220384
}
221385
}
222386
XCTAssert(true)
223-
waitForExpectations(timeout: 1)
387+
waitForExpectations(timeout: 5)
224388
}
225389

390+
#if compiler(>=5.5) && canImport(_Concurrency)
391+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
392+
func testExplicitErrorAsync() async throws {
393+
let function = functions.httpsCallable("explicitErrorTest")
394+
XCTAssertNotNil(function)
395+
do {
396+
_ = try await function.call([])
397+
} catch {
398+
let error = try XCTUnwrap(error) as NSError
399+
XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
400+
XCTAssertEqual("explicit nope", error.localizedDescription)
401+
XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
402+
error.userInfo[FunctionsErrorDetailsKey] as! [String: Int32])
403+
return
404+
}
405+
XCTAssertFalse(true, "Failed to throw error for missing result")
406+
}
407+
#endif
408+
226409
func testHttpError() {
227410
let expectation = expectation(description: #function)
228411
let function = functions.httpsCallable("httpErrorTest")
@@ -238,9 +421,25 @@ class IntegrationTests: XCTestCase {
238421
}
239422
}
240423
XCTAssert(true)
241-
waitForExpectations(timeout: 1)
424+
waitForExpectations(timeout: 5)
242425
}
243426

427+
#if compiler(>=5.5) && canImport(_Concurrency)
428+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
429+
func testHttpErrorAsync() async throws {
430+
let function = functions.httpsCallable("httpErrorTest")
431+
XCTAssertNotNil(function)
432+
do {
433+
_ = try await function.call([])
434+
} catch {
435+
let error = try XCTUnwrap(error) as NSError
436+
XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
437+
return
438+
}
439+
XCTAssertFalse(true, "Failed to throw error for missing result")
440+
}
441+
#endif
442+
244443
func testTimeout() {
245444
let expectation = expectation(description: #function)
246445
let function = functions.httpsCallable("timeoutTest")
@@ -259,6 +458,25 @@ class IntegrationTests: XCTestCase {
259458
}
260459
}
261460
XCTAssert(true)
262-
waitForExpectations(timeout: 1)
461+
waitForExpectations(timeout: 5)
263462
}
463+
464+
#if compiler(>=5.5) && canImport(_Concurrency)
465+
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
466+
func testTimeoutAsync() async throws {
467+
let function = functions.httpsCallable("timeoutTest")
468+
XCTAssertNotNil(function)
469+
function.timeoutInterval = 0.05
470+
do {
471+
_ = try await function.call([])
472+
} catch {
473+
let error = try XCTUnwrap(error) as NSError
474+
XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
475+
XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
476+
XCTAssertNil(error.userInfo[FunctionsErrorDetailsKey])
477+
return
478+
}
479+
XCTAssertFalse(true, "Failed to throw error for missing result")
480+
}
481+
#endif
264482
}

FirebaseFunctionsTestingSupport.podspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Pod::Spec.new do |s|
3535

3636
s.source_files = [
3737
base_dir + 'Sources/**/*.{m,mm,h}',
38+
'SharedTestUtilities/FIRAuthInteropFake*',
39+
'SharedTestUtilities/FIRMessagingInteropFake*'
3840
]
3941

4042
s.public_header_files = base_dir + '**/*.h'

0 commit comments

Comments
 (0)