HTTPSCallableTests.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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. import Foundation
  15. import Combine
  16. import FirebaseAppCheckInterop
  17. import FirebaseAuthInterop
  18. import FirebaseCore
  19. @testable import FirebaseFunctions
  20. import FirebaseFunctionsCombineSwift
  21. import FirebaseMessagingInterop
  22. import GTMSessionFetcherCore
  23. import XCTest
  24. // hardcoded in HTTPSCallable.swift
  25. private let timeoutInterval: TimeInterval = 70.0
  26. private let expectationTimeout: TimeInterval = 2
  27. class MockFunctions: Functions, @unchecked Sendable {
  28. let mockCallFunction: () throws -> HTTPSCallableResult
  29. var verifyParameters: ((_ url: URL, _ data: Any?, _ timeout: TimeInterval) throws -> Void)?
  30. override func callFunction(at url: URL,
  31. withObject data: Any?,
  32. options: HTTPSCallableOptions?,
  33. timeout: TimeInterval) async throws -> sending HTTPSCallableResult {
  34. try verifyParameters?(url, data, timeout)
  35. return try mockCallFunction()
  36. }
  37. init(mockCallFunction: @escaping () throws -> HTTPSCallableResult) {
  38. self.mockCallFunction = mockCallFunction
  39. super.init(
  40. projectID: "dummy-project",
  41. region: "test-region",
  42. customDomain: nil,
  43. auth: nil,
  44. messaging: nil,
  45. appCheck: nil,
  46. fetcherService: GTMSessionFetcherService()
  47. )
  48. }
  49. }
  50. public class HTTPSCallableResultFake: HTTPSCallableResult {
  51. let fakeData: String
  52. init(data: String) {
  53. fakeData = data
  54. super.init(data: data)
  55. }
  56. }
  57. @available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
  58. class HTTPSCallableTests: XCTestCase {
  59. func testCallWithoutParametersSuccess() {
  60. // given
  61. var cancellables = Set<AnyCancellable>()
  62. let httpsFunctionWasCalledExpectation = expectation(description: "HTTPS Function was called")
  63. let functionWasCalledExpectation = expectation(description: "Function was called")
  64. let expectedResult = "mockResult w/o parameters"
  65. let functions = MockFunctions {
  66. httpsFunctionWasCalledExpectation.fulfill()
  67. return HTTPSCallableResultFake(data: expectedResult)
  68. }
  69. let dummyFunction = functions.httpsCallable("dummyFunction")
  70. // when
  71. dummyFunction.call()
  72. .sink { completion in
  73. switch completion {
  74. case .finished:
  75. print("Finished")
  76. case let .failure(error):
  77. XCTFail("💥 Something went wrong: \(error)")
  78. }
  79. } receiveValue: { functionResult in
  80. guard let result = functionResult.data as? String else {
  81. XCTFail("Expected String data")
  82. return
  83. }
  84. XCTAssertEqual(result, expectedResult)
  85. functionWasCalledExpectation.fulfill()
  86. }
  87. .store(in: &cancellables)
  88. // then
  89. wait(
  90. for: [functionWasCalledExpectation, httpsFunctionWasCalledExpectation],
  91. timeout: expectationTimeout
  92. )
  93. }
  94. func testCallWithParametersSuccess() {
  95. // given
  96. var cancellables = Set<AnyCancellable>()
  97. let httpsFunctionWasCalledExpectation = expectation(description: "HTTPS Function was called")
  98. let functionWasCalledExpectation = expectation(description: "Function was called")
  99. let inputParameter = "input parameter"
  100. let expectedResult = "mockResult w/ parameters: \(inputParameter)"
  101. let functions = MockFunctions {
  102. httpsFunctionWasCalledExpectation.fulfill()
  103. return HTTPSCallableResultFake(data: expectedResult)
  104. }
  105. functions.verifyParameters = { url, data, timeout in
  106. XCTAssertEqual(
  107. url.absoluteString,
  108. "https://test-region-dummy-project.cloudfunctions.net/dummyFunction"
  109. )
  110. XCTAssertEqual(data as? String, inputParameter)
  111. XCTAssertEqual(timeout as TimeInterval, timeoutInterval)
  112. }
  113. let dummyFunction = functions.httpsCallable("dummyFunction")
  114. // when
  115. dummyFunction.call(inputParameter)
  116. .sink { completion in
  117. switch completion {
  118. case .finished:
  119. print("Finished")
  120. case let .failure(error):
  121. XCTFail("💥 Something went wrong: \(error)")
  122. }
  123. } receiveValue: { functionResult in
  124. guard let result = functionResult.data as? String else {
  125. XCTFail("Expected String data")
  126. return
  127. }
  128. XCTAssertEqual(result, expectedResult)
  129. functionWasCalledExpectation.fulfill()
  130. }
  131. .store(in: &cancellables)
  132. // then
  133. wait(
  134. for: [httpsFunctionWasCalledExpectation, functionWasCalledExpectation],
  135. timeout: expectationTimeout
  136. )
  137. }
  138. func testCallWithParametersFailure() {
  139. // given
  140. var cancellables = Set<AnyCancellable>()
  141. let httpsFunctionWasCalledExpectation = expectation(description: "HTTPS Function was called")
  142. let functionCallFailedExpectation = expectation(description: "Function call failed")
  143. let inputParameter = "input parameter"
  144. let functions = MockFunctions {
  145. httpsFunctionWasCalledExpectation.fulfill()
  146. throw NSError(domain: FunctionsErrorDomain,
  147. code: FunctionsErrorCode.internal.rawValue,
  148. userInfo: [NSLocalizedDescriptionKey: "Response is missing data field."])
  149. }
  150. functions.verifyParameters = { url, data, timeout in
  151. XCTAssertEqual(
  152. url.absoluteString,
  153. "https://test-region-dummy-project.cloudfunctions.net/dummyFunction"
  154. )
  155. XCTAssertEqual(data as? String, inputParameter)
  156. XCTAssertEqual(timeout as TimeInterval, timeoutInterval)
  157. }
  158. let dummyFunction = functions.httpsCallable("dummyFunction")
  159. // when
  160. dummyFunction.call(inputParameter)
  161. .sink { completion in
  162. if case let .failure(error as NSError) = completion {
  163. // Verify user mismatch error.
  164. XCTAssertEqual(error.code, FunctionsErrorCode.internal.rawValue)
  165. functionCallFailedExpectation.fulfill()
  166. }
  167. } receiveValue: { functionResult in
  168. XCTFail("💥 result unexpected")
  169. }
  170. .store(in: &cancellables)
  171. // then
  172. wait(
  173. for: [functionCallFailedExpectation, httpsFunctionWasCalledExpectation],
  174. timeout: expectationTimeout
  175. )
  176. }
  177. }