Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port Functions integration tests to Swift #8957

Merged
merged 3 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/functions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ jobs:
run: scripts/setup_spm_tests.sh
- name: iOS Unit Tests
run: scripts/third_party/travis/retry.sh ./scripts/build.sh FirebaseFunctions iOS spmbuildonly
- name: Integration Test Server
run: FirebaseFunctions/Backend/start.sh synchronous
- name: iOS Swift Integration Tests
run: scripts/third_party/travis/retry.sh ./scripts/build.sh FunctionsSwiftIntegration iOS spm
- name: iOS Objective C Integration Tests
run: scripts/third_party/travis/retry.sh ./scripts/build.sh FunctionsIntegration iOS spm
- name: Combine Unit Tests
run: scripts/third_party/travis/retry.sh ./scripts/build.sh FunctionsCombineUnit iOS spm

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/spm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
- uses: actions/checkout@v2
- name: Initialize xcodebuild
run: scripts/setup_spm_tests.sh
- name: Functions Integration Test Server
run: FirebaseFunctions/Backend/start.sh synchronous
- name: iOS Unit Tests
run: scripts/third_party/travis/retry.sh ./scripts/build.sh Firebase-Package iOS spm

Expand Down
264 changes: 264 additions & 0 deletions FirebaseFunctions/Tests/SwiftIntegration/IntegrationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation

import FirebaseFunctions
import FirebaseFunctionsTestingSupport
import XCTest

/// This file was intitialized as a direct port of the Objective C
/// FirebaseFunctions/Tests/Integration/FIRIntegrationTests.m
///
/// The tests require the emulator to be running with `FirebaseFunctions/Backend/start.sh synchronous`
/// The Firebase Functions called in the tests are implemented in `FirebaseFunctions/Backend/index.js`.

class IntegrationTests: XCTestCase {
paulb777 marked this conversation as resolved.
Show resolved Hide resolved
let functions = FunctionsFake(
projectID: "functions-integration-test",
region: "us-central1",
customDomain: nil,
withToken: nil
)
let projectID = "functions-swift-integration-test"

override func setUp() {
super.setUp()
functions.useLocalhost()
}

func testData() {
let expectation = expectation(description: #function)
let data = [
"bool": true,
"int": 2 as Int32,
"long": 9_876_543_210,
"string": "four",
"array": [5 as Int32, 6 as Int32],
"null": nil,
] as [String: Any?]
let function = functions.httpsCallable("dataTest")
XCTAssertNotNil(function)
function.call(data) { result, error in
do {
XCTAssertNil(error)
let data = try XCTUnwrap(result?.data as? [String: Any])
let message = try XCTUnwrap(data["message"] as? String)
let long = try XCTUnwrap(data["long"] as? Int64)
let code = try XCTUnwrap(data["code"] as? Int32)
XCTAssertEqual(message, "stub response")
XCTAssertEqual(long, 420)
XCTAssertEqual(code, 42)
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
waitForExpectations(timeout: 1)
}

func testScalar() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("scalarTest")
XCTAssertNotNil(function)
function.call(17 as Int16) { result, error in
do {
XCTAssertNil(error)
let data = try XCTUnwrap(result?.data as? Int)
XCTAssertEqual(data, 76)
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
waitForExpectations(timeout: 1)
}

func testToken() {
// Recreate _functions with a token.
let functions = FunctionsFake(
projectID: "functions-integration-test",
region: "us-central1",
customDomain: nil,
withToken: "token"
)
functions.useLocalhost()

let expectation = expectation(description: #function)
let function = functions.httpsCallable("FCMTokenTest")
XCTAssertNotNil(function)
function.call([:]) { result, error in
do {
XCTAssertNil(error)
let data = try XCTUnwrap(result?.data) as? [String: Int]
XCTAssertEqual(data, [:])
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
waitForExpectations(timeout: 1)
}

func testFCMToken() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("FCMTokenTest")
XCTAssertNotNil(function)
function.call([:]) { result, error in
do {
XCTAssertNil(error)
let data = try XCTUnwrap(result?.data) as? [String: Int]
XCTAssertEqual(data, [:])
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
waitForExpectations(timeout: 1)
}

func testNull() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("nullTest")
XCTAssertNotNil(function)
function.call(nil) { result, error in
do {
XCTAssertNil(error)
let data = try XCTUnwrap(result?.data) as? NSNull
XCTAssertEqual(data, NSNull())
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
waitForExpectations(timeout: 1)
}

func testMissingResult() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("missingResultTest")
XCTAssertNotNil(function)
function.call(nil) { result, error in
do {
XCTAssertNotNil(error)
let error = try XCTUnwrap(error) as NSError
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
XCTAssertEqual("Response is missing data field.", error.localizedDescription)
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
Comment on lines +153 to +161
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. You can avoid the do {} catch {} blocks by adding throws to the test method signature. If the XCTUnwrap fails, it'll highlight the line with the failing unwrap.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it will work if XCTUnwrap is used inside of the closure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right @maksymmalyhin That's what I found:

Screen Shot 2021-11-11 at 8 40 56 AM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, never thought about that. Makes sense, thanks for pointing that out y'all!

}
XCTAssert(true)
waitForExpectations(timeout: 1)
}

func testUnhandledError() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("unhandledErrorTest")
XCTAssertNotNil(function)
function.call([]) { result, error in
do {
XCTAssertNotNil(error)
let error = try XCTUnwrap(error! as NSError)
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
XCTAssertEqual("INTERNAL", error.localizedDescription)
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
XCTAssert(true)
waitForExpectations(timeout: 1)
}

func testUnknownError() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("unknownErrorTest")
XCTAssertNotNil(function)
function.call([]) { result, error in
do {
XCTAssertNotNil(error)
let error = try XCTUnwrap(error! as NSError)
XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
XCTAssertEqual("INTERNAL", error.localizedDescription)
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
XCTAssert(true)
waitForExpectations(timeout: 1)
}

func testExplicitError() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("explicitErrorTest")
XCTAssertNotNil(function)
function.call([]) { result, error in
do {
XCTAssertNotNil(error)
let error = try XCTUnwrap(error! as NSError)
XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
XCTAssertEqual("explicit nope", error.localizedDescription)
XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
error.userInfo[FunctionsErrorDetailsKey] as! [String: Int32])
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
XCTAssert(true)
waitForExpectations(timeout: 1)
}

func testHttpError() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("httpErrorTest")
XCTAssertNotNil(function)
function.call([]) { result, error in
do {
XCTAssertNotNil(error)
let error = try XCTUnwrap(error! as NSError)
XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
XCTAssert(true)
waitForExpectations(timeout: 1)
}

func testTimeout() {
let expectation = expectation(description: #function)
let function = functions.httpsCallable("timeoutTest")
XCTAssertNotNil(function)
function.timeoutInterval = 0.05
function.call([]) { result, error in
do {
XCTAssertNotNil(error)
let error = try XCTUnwrap(error! as NSError)
XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
XCTAssertNil(error.userInfo[FunctionsErrorDetailsKey])
expectation.fulfill()
} catch {
XCTAssert(false, "Failed to unwrap the function result: \(error)")
}
}
XCTAssert(true)
waitForExpectations(timeout: 1)
}
}
36 changes: 36 additions & 0 deletions FirebaseTestingSupport/Functions/Sources/FIRFunctionsFake.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#import "FirebaseTestingSupport/Functions/Sources/Public/FirebaseFunctionsTestingSupport/FIRFunctionsFake.h"
#import "FirebaseTestingSupport/Functions/Sources/Public/FirebaseFunctionsTestingSupport/FIRFunctions+Testing.h"
#import "SharedTestUtilities/FIRAuthInteropFake.h"
#import "SharedTestUtilities/FIRMessagingInteropFake.h"

@implementation FIRFunctionsFake

- (instancetype)initWithProjectID:(NSString *)projectID
region:(NSString *)region
customDomain:(nullable NSString *)customDomain
withToken:(nullable NSString *)token {
return [super initWithProjectID:projectID
region:region
customDomain:customDomain
auth:[[FIRAuthInteropFake alloc] initWithToken:token
userID:nil
error:nil]
messaging:[[FIRMessagingInteropFake alloc] init]
appCheck:nil];
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#import <Foundation/Foundation.h>

#import <FirebaseFunctions/FIRFunctions.h>

NS_ASSUME_NONNULL_BEGIN

/// A functions object with fake tokens.
NS_SWIFT_NAME(FunctionsFake)
@interface FIRFunctionsFake : FIRFunctions

/**
* Internal initializer for testing a Cloud Functions client with fakes.
* @param projectID The project ID for the Firebase project.
* @param region The region for the http trigger, such as "us-central1".
* @param customDomain A custom domain for the http trigger, such as "https://mydomain.com".
* @param token A token to use for validation (optional).
*/
- (instancetype)initWithProjectID:(NSString *)projectID
region:(NSString *)region
customDomain:(nullable NSString *)customDomain
withToken:(nullable NSString *)token;
@end

NS_ASSUME_NONNULL_END
18 changes: 18 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,22 @@ let package = Package(
dependencies: ["FirebaseFunctions"],
path: "FirebaseFunctions/Tests/SwiftUnit"
),
.testTarget(
name: "FunctionsIntegration",
dependencies: ["FirebaseFunctions",
"SharedTestUtilities"],
path: "FirebaseFunctions/Tests/Integration",
cSettings: [
.headerSearchPath("../../../"),
]
),
.testTarget(
name: "FunctionsSwiftIntegration",
dependencies: ["FirebaseFunctions",
"FirebaseFunctionsTestingSupport",
"SharedTestUtilities"],
path: "FirebaseFunctions/Tests/SwiftIntegration"
),
.target(
name: "FirebaseFunctionsTestingSupport",
dependencies: ["FirebaseFunctions"],
Expand All @@ -704,6 +720,8 @@ let package = Package(
]
),

// MARK: - Firebase In App Messaging

.target(
name: "FirebaseInAppMessagingTarget",
dependencies: [
Expand Down