Skip to content

Commit f023aa5

Browse files
authored
Port Functions integration tests to Swift (#8957)
1 parent 8256dde commit f023aa5

File tree

8 files changed

+468
-0
lines changed

8 files changed

+468
-0
lines changed

.github/workflows/functions.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ jobs:
4747
run: scripts/setup_spm_tests.sh
4848
- name: iOS Unit Tests
4949
run: scripts/third_party/travis/retry.sh ./scripts/build.sh FirebaseFunctions iOS spmbuildonly
50+
- name: Integration Test Server
51+
run: FirebaseFunctions/Backend/start.sh synchronous
52+
- name: iOS Swift Integration Tests
53+
run: scripts/third_party/travis/retry.sh ./scripts/build.sh FunctionsSwiftIntegration iOS spm
54+
- name: iOS Objective C Integration Tests
55+
run: scripts/third_party/travis/retry.sh ./scripts/build.sh FunctionsIntegration iOS spm
5056
- name: Combine Unit Tests
5157
run: scripts/third_party/travis/retry.sh ./scripts/build.sh FunctionsCombineUnit iOS spm
5258

.github/workflows/spm.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ jobs:
2525
- uses: actions/checkout@v2
2626
- name: Initialize xcodebuild
2727
run: scripts/setup_spm_tests.sh
28+
- name: Functions Integration Test Server
29+
run: FirebaseFunctions/Backend/start.sh synchronous
2830
- name: iOS Unit Tests
2931
run: scripts/third_party/travis/retry.sh ./scripts/build.sh Firebase-Package iOS spm
3032

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
17+
import FirebaseFunctions
18+
import FirebaseFunctionsTestingSupport
19+
import XCTest
20+
21+
/// This file was intitialized as a direct port of the Objective C
22+
/// FirebaseFunctions/Tests/Integration/FIRIntegrationTests.m
23+
///
24+
/// The tests require the emulator to be running with `FirebaseFunctions/Backend/start.sh synchronous`
25+
/// The Firebase Functions called in the tests are implemented in `FirebaseFunctions/Backend/index.js`.
26+
27+
class IntegrationTests: XCTestCase {
28+
let functions = FunctionsFake(
29+
projectID: "functions-integration-test",
30+
region: "us-central1",
31+
customDomain: nil,
32+
withToken: nil
33+
)
34+
let projectID = "functions-swift-integration-test"
35+
36+
override func setUp() {
37+
super.setUp()
38+
functions.useLocalhost()
39+
}
40+
41+
func testData() {
42+
let expectation = expectation(description: #function)
43+
let data = [
44+
"bool": true,
45+
"int": 2 as Int32,
46+
"long": 9_876_543_210,
47+
"string": "four",
48+
"array": [5 as Int32, 6 as Int32],
49+
"null": nil,
50+
] as [String: Any?]
51+
let function = functions.httpsCallable("dataTest")
52+
XCTAssertNotNil(function)
53+
function.call(data) { result, error in
54+
do {
55+
XCTAssertNil(error)
56+
let data = try XCTUnwrap(result?.data as? [String: Any])
57+
let message = try XCTUnwrap(data["message"] as? String)
58+
let long = try XCTUnwrap(data["long"] as? Int64)
59+
let code = try XCTUnwrap(data["code"] as? Int32)
60+
XCTAssertEqual(message, "stub response")
61+
XCTAssertEqual(long, 420)
62+
XCTAssertEqual(code, 42)
63+
expectation.fulfill()
64+
} catch {
65+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
66+
}
67+
}
68+
waitForExpectations(timeout: 1)
69+
}
70+
71+
func testScalar() {
72+
let expectation = expectation(description: #function)
73+
let function = functions.httpsCallable("scalarTest")
74+
XCTAssertNotNil(function)
75+
function.call(17 as Int16) { result, error in
76+
do {
77+
XCTAssertNil(error)
78+
let data = try XCTUnwrap(result?.data as? Int)
79+
XCTAssertEqual(data, 76)
80+
expectation.fulfill()
81+
} catch {
82+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
83+
}
84+
}
85+
waitForExpectations(timeout: 1)
86+
}
87+
88+
func testToken() {
89+
// Recreate _functions with a token.
90+
let functions = FunctionsFake(
91+
projectID: "functions-integration-test",
92+
region: "us-central1",
93+
customDomain: nil,
94+
withToken: "token"
95+
)
96+
functions.useLocalhost()
97+
98+
let expectation = expectation(description: #function)
99+
let function = functions.httpsCallable("FCMTokenTest")
100+
XCTAssertNotNil(function)
101+
function.call([:]) { result, error in
102+
do {
103+
XCTAssertNil(error)
104+
let data = try XCTUnwrap(result?.data) as? [String: Int]
105+
XCTAssertEqual(data, [:])
106+
expectation.fulfill()
107+
} catch {
108+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
109+
}
110+
}
111+
waitForExpectations(timeout: 1)
112+
}
113+
114+
func testFCMToken() {
115+
let expectation = expectation(description: #function)
116+
let function = functions.httpsCallable("FCMTokenTest")
117+
XCTAssertNotNil(function)
118+
function.call([:]) { result, error in
119+
do {
120+
XCTAssertNil(error)
121+
let data = try XCTUnwrap(result?.data) as? [String: Int]
122+
XCTAssertEqual(data, [:])
123+
expectation.fulfill()
124+
} catch {
125+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
126+
}
127+
}
128+
waitForExpectations(timeout: 1)
129+
}
130+
131+
func testNull() {
132+
let expectation = expectation(description: #function)
133+
let function = functions.httpsCallable("nullTest")
134+
XCTAssertNotNil(function)
135+
function.call(nil) { result, error in
136+
do {
137+
XCTAssertNil(error)
138+
let data = try XCTUnwrap(result?.data) as? NSNull
139+
XCTAssertEqual(data, NSNull())
140+
expectation.fulfill()
141+
} catch {
142+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
143+
}
144+
}
145+
waitForExpectations(timeout: 1)
146+
}
147+
148+
func testMissingResult() {
149+
let expectation = expectation(description: #function)
150+
let function = functions.httpsCallable("missingResultTest")
151+
XCTAssertNotNil(function)
152+
function.call(nil) { result, error in
153+
do {
154+
XCTAssertNotNil(error)
155+
let error = try XCTUnwrap(error) as NSError
156+
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
157+
XCTAssertEqual("Response is missing data field.", error.localizedDescription)
158+
expectation.fulfill()
159+
} catch {
160+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
161+
}
162+
}
163+
XCTAssert(true)
164+
waitForExpectations(timeout: 1)
165+
}
166+
167+
func testUnhandledError() {
168+
let expectation = expectation(description: #function)
169+
let function = functions.httpsCallable("unhandledErrorTest")
170+
XCTAssertNotNil(function)
171+
function.call([]) { result, error in
172+
do {
173+
XCTAssertNotNil(error)
174+
let error = try XCTUnwrap(error! as NSError)
175+
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
176+
XCTAssertEqual("INTERNAL", error.localizedDescription)
177+
expectation.fulfill()
178+
} catch {
179+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
180+
}
181+
}
182+
XCTAssert(true)
183+
waitForExpectations(timeout: 1)
184+
}
185+
186+
func testUnknownError() {
187+
let expectation = expectation(description: #function)
188+
let function = functions.httpsCallable("unknownErrorTest")
189+
XCTAssertNotNil(function)
190+
function.call([]) { result, error in
191+
do {
192+
XCTAssertNotNil(error)
193+
let error = try XCTUnwrap(error! as NSError)
194+
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
195+
XCTAssertEqual("INTERNAL", error.localizedDescription)
196+
expectation.fulfill()
197+
} catch {
198+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
199+
}
200+
}
201+
XCTAssert(true)
202+
waitForExpectations(timeout: 1)
203+
}
204+
205+
func testExplicitError() {
206+
let expectation = expectation(description: #function)
207+
let function = functions.httpsCallable("explicitErrorTest")
208+
XCTAssertNotNil(function)
209+
function.call([]) { result, error in
210+
do {
211+
XCTAssertNotNil(error)
212+
let error = try XCTUnwrap(error! as NSError)
213+
XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
214+
XCTAssertEqual("explicit nope", error.localizedDescription)
215+
XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
216+
error.userInfo[FunctionsErrorDetailsKey] as! [String: Int32])
217+
expectation.fulfill()
218+
} catch {
219+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
220+
}
221+
}
222+
XCTAssert(true)
223+
waitForExpectations(timeout: 1)
224+
}
225+
226+
func testHttpError() {
227+
let expectation = expectation(description: #function)
228+
let function = functions.httpsCallable("httpErrorTest")
229+
XCTAssertNotNil(function)
230+
function.call([]) { result, error in
231+
do {
232+
XCTAssertNotNil(error)
233+
let error = try XCTUnwrap(error! as NSError)
234+
XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
235+
expectation.fulfill()
236+
} catch {
237+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
238+
}
239+
}
240+
XCTAssert(true)
241+
waitForExpectations(timeout: 1)
242+
}
243+
244+
func testTimeout() {
245+
let expectation = expectation(description: #function)
246+
let function = functions.httpsCallable("timeoutTest")
247+
XCTAssertNotNil(function)
248+
function.timeoutInterval = 0.05
249+
function.call([]) { result, error in
250+
do {
251+
XCTAssertNotNil(error)
252+
let error = try XCTUnwrap(error! as NSError)
253+
XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
254+
XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
255+
XCTAssertNil(error.userInfo[FunctionsErrorDetailsKey])
256+
expectation.fulfill()
257+
} catch {
258+
XCTAssert(false, "Failed to unwrap the function result: \(error)")
259+
}
260+
}
261+
XCTAssert(true)
262+
waitForExpectations(timeout: 1)
263+
}
264+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import "FirebaseTestingSupport/Functions/Sources/Public/FirebaseFunctionsTestingSupport/FIRFunctionsFake.h"
16+
#import "FirebaseTestingSupport/Functions/Sources/Public/FirebaseFunctionsTestingSupport/FIRFunctions+Testing.h"
17+
#import "SharedTestUtilities/FIRAuthInteropFake.h"
18+
#import "SharedTestUtilities/FIRMessagingInteropFake.h"
19+
20+
@implementation FIRFunctionsFake
21+
22+
- (instancetype)initWithProjectID:(NSString *)projectID
23+
region:(NSString *)region
24+
customDomain:(nullable NSString *)customDomain
25+
withToken:(nullable NSString *)token {
26+
return [super initWithProjectID:projectID
27+
region:region
28+
customDomain:customDomain
29+
auth:[[FIRAuthInteropFake alloc] initWithToken:token
30+
userID:nil
31+
error:nil]
32+
messaging:[[FIRMessagingInteropFake alloc] init]
33+
appCheck:nil];
34+
}
35+
36+
@end
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import <Foundation/Foundation.h>
16+
17+
#import <FirebaseFunctions/FIRFunctions.h>
18+
19+
NS_ASSUME_NONNULL_BEGIN
20+
21+
/// A functions object with fake tokens.
22+
NS_SWIFT_NAME(FunctionsFake)
23+
@interface FIRFunctionsFake : FIRFunctions
24+
25+
/**
26+
* Internal initializer for testing a Cloud Functions client with fakes.
27+
* @param projectID The project ID for the Firebase project.
28+
* @param region The region for the http trigger, such as "us-central1".
29+
* @param customDomain A custom domain for the http trigger, such as "https://mydomain.com".
30+
* @param token A token to use for validation (optional).
31+
*/
32+
- (instancetype)initWithProjectID:(NSString *)projectID
33+
region:(NSString *)region
34+
customDomain:(nullable NSString *)customDomain
35+
withToken:(nullable NSString *)token;
36+
@end
37+
38+
NS_ASSUME_NONNULL_END

Package.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,22 @@ let package = Package(
694694
dependencies: ["FirebaseFunctions"],
695695
path: "FirebaseFunctions/Tests/SwiftUnit"
696696
),
697+
.testTarget(
698+
name: "FunctionsIntegration",
699+
dependencies: ["FirebaseFunctions",
700+
"SharedTestUtilities"],
701+
path: "FirebaseFunctions/Tests/Integration",
702+
cSettings: [
703+
.headerSearchPath("../../../"),
704+
]
705+
),
706+
.testTarget(
707+
name: "FunctionsSwiftIntegration",
708+
dependencies: ["FirebaseFunctions",
709+
"FirebaseFunctionsTestingSupport",
710+
"SharedTestUtilities"],
711+
path: "FirebaseFunctions/Tests/SwiftIntegration"
712+
),
697713
.target(
698714
name: "FirebaseFunctionsTestingSupport",
699715
dependencies: ["FirebaseFunctions"],
@@ -704,6 +720,8 @@ let package = Package(
704720
]
705721
),
706722

723+
// MARK: - Firebase In App Messaging
724+
707725
.target(
708726
name: "FirebaseInAppMessagingTarget",
709727
dependencies: [

0 commit comments

Comments
 (0)