IntegrationTests.swift 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  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. @testable import FirebaseFunctions
  16. import FirebaseAuthInterop
  17. import FirebaseMessagingInterop
  18. import XCTest
  19. /// This file was intitialized as a direct port of `FirebaseFunctionsSwift/Tests/IntegrationTests.swift`
  20. /// which itself was ported from the Objective-C `FirebaseFunctions/Tests/Integration/FIRIntegrationTests.m`
  21. ///
  22. /// The tests require the emulator to be running with `FirebaseFunctions/Backend/start.sh synchronous`
  23. /// The Firebase Functions called in the tests are implemented in `FirebaseFunctions/Backend/index.js`.
  24. struct DataTestRequest: Encodable {
  25. var bool: Bool
  26. var int: Int32
  27. var long: Int64
  28. var string: String
  29. var array: [Int32]
  30. // NOTE: Auto-synthesized Encodable conformance uses 'encodeIfPresent' to
  31. // encode Optional values. To encode Optional.none as null you either need
  32. // to write a manual encodable conformance or use a helper like the
  33. // propertyWrapper here:
  34. @NullEncodable var null: Bool?
  35. }
  36. @propertyWrapper
  37. struct NullEncodable<T>: Encodable where T: Encodable {
  38. var wrappedValue: T?
  39. init(wrappedValue: T?) {
  40. self.wrappedValue = wrappedValue
  41. }
  42. func encode(to encoder: Encoder) throws {
  43. var container = encoder.singleValueContainer()
  44. switch wrappedValue {
  45. case let .some(value): try container.encode(value)
  46. case .none: try container.encodeNil()
  47. }
  48. }
  49. }
  50. struct DataTestResponse: Decodable, Equatable {
  51. var message: String
  52. var long: Int64
  53. var code: Int32
  54. }
  55. class IntegrationTests: XCTestCase {
  56. let functions = Functions(projectID: "functions-integration-test",
  57. region: "us-central1",
  58. customDomain: nil,
  59. auth: nil,
  60. messaging: MessagingTokenProvider(),
  61. appCheck: nil)
  62. override func setUp() {
  63. super.setUp()
  64. functions.useEmulator(withHost: "localhost", port: 5005)
  65. }
  66. func emulatorURL(_ funcName: String) -> URL {
  67. return URL(string: "http://localhost:5005/functions-integration-test/us-central1/\(funcName)")!
  68. }
  69. func testData() {
  70. let data = DataTestRequest(
  71. bool: true,
  72. int: 2,
  73. long: 9_876_543_210,
  74. string: "four",
  75. array: [5, 6],
  76. null: nil
  77. )
  78. let byName = functions.httpsCallable("dataTest",
  79. requestAs: DataTestRequest.self,
  80. responseAs: DataTestResponse.self)
  81. let byURL = functions.httpsCallable(emulatorURL("dataTest"),
  82. requestAs: DataTestRequest.self,
  83. responseAs: DataTestResponse.self)
  84. for function in [byName, byURL] {
  85. let expectation = expectation(description: #function)
  86. function.call(data) { result in
  87. do {
  88. let response = try result.get()
  89. let expected = DataTestResponse(
  90. message: "stub response",
  91. long: 420,
  92. code: 42
  93. )
  94. XCTAssertEqual(response, expected)
  95. } catch {
  96. XCTFail("Failed to unwrap the function result: \(error)")
  97. }
  98. expectation.fulfill()
  99. }
  100. waitForExpectations(timeout: 5)
  101. }
  102. }
  103. #if compiler(>=5.5.2) && canImport(_Concurrency)
  104. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  105. func testDataAsync() async throws {
  106. let data = DataTestRequest(
  107. bool: true,
  108. int: 2,
  109. long: 9_876_543_210,
  110. string: "four",
  111. array: [5, 6],
  112. null: nil
  113. )
  114. let byName = functions.httpsCallable("dataTest",
  115. requestAs: DataTestRequest.self,
  116. responseAs: DataTestResponse.self)
  117. let byUrl = functions.httpsCallable(emulatorURL("dataTest"),
  118. requestAs: DataTestRequest.self,
  119. responseAs: DataTestResponse.self)
  120. for function in [byName, byUrl] {
  121. let response = try await function.call(data)
  122. let expected = DataTestResponse(
  123. message: "stub response",
  124. long: 420,
  125. code: 42
  126. )
  127. XCTAssertEqual(response, expected)
  128. }
  129. }
  130. #endif
  131. func testScalar() {
  132. let byName = functions.httpsCallable(
  133. "scalarTest",
  134. requestAs: Int16.self,
  135. responseAs: Int.self
  136. )
  137. let byURL = functions.httpsCallable(
  138. emulatorURL("scalarTest"),
  139. requestAs: Int16.self,
  140. responseAs: Int.self
  141. )
  142. for function in [byName, byURL] {
  143. let expectation = expectation(description: #function)
  144. function.call(17) { result in
  145. do {
  146. let response = try result.get()
  147. XCTAssertEqual(response, 76)
  148. } catch {
  149. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  150. }
  151. expectation.fulfill()
  152. }
  153. waitForExpectations(timeout: 5)
  154. }
  155. }
  156. #if compiler(>=5.5.2) && canImport(_Concurrency)
  157. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  158. func testScalarAsync() async throws {
  159. let byName = functions.httpsCallable(
  160. "scalarTest",
  161. requestAs: Int16.self,
  162. responseAs: Int.self
  163. )
  164. let byURL = functions.httpsCallable(
  165. emulatorURL("scalarTest"),
  166. requestAs: Int16.self,
  167. responseAs: Int.self
  168. )
  169. for function in [byName, byURL] {
  170. let result = try await function.call(17)
  171. XCTAssertEqual(result, 76)
  172. }
  173. }
  174. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  175. func testScalarAsyncAlternateSignature() async throws {
  176. let byName: Callable<Int16, Int> = functions.httpsCallable("scalarTest")
  177. let byURL: Callable<Int16, Int> = functions.httpsCallable(emulatorURL("scalarTest"))
  178. for function in [byName, byURL] {
  179. let result = try await function.call(17)
  180. XCTAssertEqual(result, 76)
  181. }
  182. }
  183. #endif
  184. func testToken() {
  185. // Recreate functions with a token.
  186. let functions = Functions(
  187. projectID: "functions-integration-test",
  188. region: "us-central1",
  189. customDomain: nil,
  190. auth: AuthTokenProvider(token: "token"),
  191. messaging: MessagingTokenProvider(),
  192. appCheck: nil
  193. )
  194. functions.useEmulator(withHost: "localhost", port: 5005)
  195. let byName = functions.httpsCallable(
  196. "tokenTest",
  197. requestAs: [String: Int].self,
  198. responseAs: [String: Int].self
  199. )
  200. let byURL = functions.httpsCallable(
  201. emulatorURL("tokenTest"),
  202. requestAs: [String: Int].self,
  203. responseAs: [String: Int].self
  204. )
  205. for function in [byName, byURL] {
  206. let expectation = expectation(description: #function)
  207. XCTAssertNotNil(function)
  208. function.call([:]) { result in
  209. do {
  210. let data = try result.get()
  211. XCTAssertEqual(data, [:])
  212. } catch {
  213. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  214. }
  215. expectation.fulfill()
  216. }
  217. waitForExpectations(timeout: 5)
  218. }
  219. }
  220. #if compiler(>=5.5.2) && canImport(_Concurrency)
  221. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  222. func testTokenAsync() async throws {
  223. // Recreate functions with a token.
  224. let functions = Functions(
  225. projectID: "functions-integration-test",
  226. region: "us-central1",
  227. customDomain: nil,
  228. auth: AuthTokenProvider(token: "token"),
  229. messaging: MessagingTokenProvider(),
  230. appCheck: nil
  231. )
  232. functions.useEmulator(withHost: "localhost", port: 5005)
  233. let byName = functions.httpsCallable(
  234. "tokenTest",
  235. requestAs: [String: Int].self,
  236. responseAs: [String: Int].self
  237. )
  238. let byURL = functions.httpsCallable(
  239. emulatorURL("tokenTest"),
  240. requestAs: [String: Int].self,
  241. responseAs: [String: Int].self
  242. )
  243. for function in [byName, byURL] {
  244. let data = try await function.call([:])
  245. XCTAssertEqual(data, [:])
  246. }
  247. }
  248. #endif
  249. func testFCMToken() {
  250. let byName = functions.httpsCallable(
  251. "FCMTokenTest",
  252. requestAs: [String: Int].self,
  253. responseAs: [String: Int].self
  254. )
  255. let byURL = functions.httpsCallable(
  256. emulatorURL("FCMTokenTest"),
  257. requestAs: [String: Int].self,
  258. responseAs: [String: Int].self
  259. )
  260. for function in [byName, byURL] {
  261. let expectation = expectation(description: #function)
  262. function.call([:]) { result in
  263. do {
  264. let data = try result.get()
  265. XCTAssertEqual(data, [:])
  266. } catch {
  267. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  268. }
  269. expectation.fulfill()
  270. }
  271. waitForExpectations(timeout: 5)
  272. }
  273. }
  274. #if compiler(>=5.5.2) && canImport(_Concurrency)
  275. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  276. func testFCMTokenAsync() async throws {
  277. let byName = functions.httpsCallable(
  278. "FCMTokenTest",
  279. requestAs: [String: Int].self,
  280. responseAs: [String: Int].self
  281. )
  282. let byURL = functions.httpsCallable(
  283. emulatorURL("FCMTokenTest"),
  284. requestAs: [String: Int].self,
  285. responseAs: [String: Int].self
  286. )
  287. for function in [byName, byURL] {
  288. let data = try await function.call([:])
  289. XCTAssertEqual(data, [:])
  290. }
  291. }
  292. #endif
  293. func testNull() {
  294. let byName = functions.httpsCallable(
  295. "nullTest",
  296. requestAs: Int?.self,
  297. responseAs: Int?.self
  298. )
  299. let byURL = functions.httpsCallable(
  300. emulatorURL("nullTest"),
  301. requestAs: Int?.self,
  302. responseAs: Int?.self
  303. )
  304. for function in [byName, byURL] {
  305. let expectation = expectation(description: #function)
  306. function.call(nil) { result in
  307. do {
  308. let data = try result.get()
  309. XCTAssertEqual(data, nil)
  310. } catch {
  311. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  312. }
  313. expectation.fulfill()
  314. }
  315. waitForExpectations(timeout: 5)
  316. }
  317. }
  318. #if compiler(>=5.5.2) && canImport(_Concurrency)
  319. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  320. func testNullAsync() async throws {
  321. let byName = functions.httpsCallable(
  322. "nullTest",
  323. requestAs: Int?.self,
  324. responseAs: Int?.self
  325. )
  326. let byURL = functions.httpsCallable(
  327. emulatorURL("nullTest"),
  328. requestAs: Int?.self,
  329. responseAs: Int?.self
  330. )
  331. for function in [byName, byURL] {
  332. let data = try await function.call(nil)
  333. XCTAssertEqual(data, nil)
  334. }
  335. }
  336. #endif
  337. func testMissingResult() {
  338. let byName = functions.httpsCallable(
  339. "missingResultTest",
  340. requestAs: Int?.self,
  341. responseAs: Int?.self
  342. )
  343. let byURL = functions.httpsCallable(
  344. emulatorURL("missingResultTest"),
  345. requestAs: Int?.self,
  346. responseAs: Int?.self
  347. )
  348. for function in [byName, byURL] {
  349. let expectation = expectation(description: #function)
  350. function.call(nil) { result in
  351. do {
  352. _ = try result.get()
  353. } catch {
  354. let error = error as NSError
  355. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  356. XCTAssertEqual("Response is missing data field.", error.localizedDescription)
  357. expectation.fulfill()
  358. return
  359. }
  360. XCTFail("Failed to throw error for missing result")
  361. }
  362. waitForExpectations(timeout: 5)
  363. }
  364. }
  365. #if compiler(>=5.5.2) && canImport(_Concurrency)
  366. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  367. func testMissingResultAsync() async {
  368. let byName = functions.httpsCallable(
  369. "missingResultTest",
  370. requestAs: Int?.self,
  371. responseAs: Int?.self
  372. )
  373. let byURL = functions.httpsCallable(
  374. emulatorURL("missingResultTest"),
  375. requestAs: Int?.self,
  376. responseAs: Int?.self
  377. )
  378. for function in [byName, byURL] {
  379. do {
  380. _ = try await function.call(nil)
  381. XCTFail("Failed to throw error for missing result")
  382. } catch {
  383. let error = error as NSError
  384. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  385. XCTAssertEqual("Response is missing data field.", error.localizedDescription)
  386. }
  387. }
  388. }
  389. #endif
  390. func testUnhandledError() {
  391. let byName = functions.httpsCallable(
  392. "unhandledErrorTest",
  393. requestAs: [Int].self,
  394. responseAs: Int.self
  395. )
  396. let byURL = functions.httpsCallable(
  397. emulatorURL("unhandledErrorTest"),
  398. requestAs: [Int].self,
  399. responseAs: Int.self
  400. )
  401. for function in [byName, byURL] {
  402. let expectation = expectation(description: #function)
  403. function.call([]) { result in
  404. do {
  405. _ = try result.get()
  406. } catch {
  407. let error = error as NSError
  408. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  409. XCTAssertEqual("INTERNAL", error.localizedDescription)
  410. expectation.fulfill()
  411. return
  412. }
  413. XCTFail("Failed to throw error for missing result")
  414. }
  415. XCTAssert(true)
  416. waitForExpectations(timeout: 5)
  417. }
  418. }
  419. #if compiler(>=5.5.2) && canImport(_Concurrency)
  420. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  421. func testUnhandledErrorAsync() async {
  422. let byName = functions.httpsCallable(
  423. "unhandledErrorTest",
  424. requestAs: [Int].self,
  425. responseAs: Int.self
  426. )
  427. let byURL = functions.httpsCallable(
  428. "unhandledErrorTest",
  429. requestAs: [Int].self,
  430. responseAs: Int.self
  431. )
  432. for function in [byName, byURL] {
  433. do {
  434. _ = try await function.call([])
  435. XCTFail("Failed to throw error for missing result")
  436. } catch {
  437. let error = error as NSError
  438. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  439. XCTAssertEqual("INTERNAL", error.localizedDescription)
  440. }
  441. }
  442. }
  443. #endif
  444. func testUnknownError() {
  445. let byName = functions.httpsCallable(
  446. "unknownErrorTest",
  447. requestAs: [Int].self,
  448. responseAs: Int.self
  449. )
  450. let byURL = functions.httpsCallable(
  451. emulatorURL("unknownErrorTest"),
  452. requestAs: [Int].self,
  453. responseAs: Int.self
  454. )
  455. for function in [byName, byURL] {
  456. let expectation = expectation(description: #function)
  457. function.call([]) { result in
  458. do {
  459. _ = try result.get()
  460. } catch {
  461. let error = error as NSError
  462. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  463. XCTAssertEqual("INTERNAL", error.localizedDescription)
  464. expectation.fulfill()
  465. return
  466. }
  467. XCTFail("Failed to throw error for missing result")
  468. }
  469. }
  470. waitForExpectations(timeout: 5)
  471. }
  472. #if compiler(>=5.5.2) && canImport(_Concurrency)
  473. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  474. func testUnknownErrorAsync() async {
  475. let byName = functions.httpsCallable(
  476. "unknownErrorTest",
  477. requestAs: [Int].self,
  478. responseAs: Int.self
  479. )
  480. let byURL = functions.httpsCallable(
  481. emulatorURL("unknownErrorTest"),
  482. requestAs: [Int].self,
  483. responseAs: Int.self
  484. )
  485. for function in [byName, byURL] {
  486. do {
  487. _ = try await function.call([])
  488. XCTAssertFalse(true, "Failed to throw error for missing result")
  489. } catch {
  490. let error = error as NSError
  491. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  492. XCTAssertEqual("INTERNAL", error.localizedDescription)
  493. }
  494. }
  495. }
  496. #endif
  497. func testExplicitError() {
  498. let byName = functions.httpsCallable(
  499. "explicitErrorTest",
  500. requestAs: [Int].self,
  501. responseAs: Int.self
  502. )
  503. let byURL = functions.httpsCallable(
  504. "explicitErrorTest",
  505. requestAs: [Int].self,
  506. responseAs: Int.self
  507. )
  508. for function in [byName, byURL] {
  509. let expectation = expectation(description: #function)
  510. function.call([]) { result in
  511. do {
  512. _ = try result.get()
  513. } catch {
  514. let error = error as NSError
  515. XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
  516. XCTAssertEqual("explicit nope", error.localizedDescription)
  517. XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
  518. error.userInfo["details"] as! [String: Int32])
  519. expectation.fulfill()
  520. return
  521. }
  522. XCTFail("Failed to throw error for missing result")
  523. }
  524. waitForExpectations(timeout: 5)
  525. }
  526. }
  527. #if compiler(>=5.5.2) && canImport(_Concurrency)
  528. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  529. func testExplicitErrorAsync() async {
  530. let byName = functions.httpsCallable(
  531. "explicitErrorTest",
  532. requestAs: [Int].self,
  533. responseAs: Int.self
  534. )
  535. let byURL = functions.httpsCallable(
  536. emulatorURL("explicitErrorTest"),
  537. requestAs: [Int].self,
  538. responseAs: Int.self
  539. )
  540. for function in [byName, byURL] {
  541. do {
  542. _ = try await function.call([])
  543. XCTAssertFalse(true, "Failed to throw error for missing result")
  544. } catch {
  545. let error = error as NSError
  546. XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
  547. XCTAssertEqual("explicit nope", error.localizedDescription)
  548. XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
  549. error.userInfo["details"] as! [String: Int32])
  550. }
  551. }
  552. }
  553. #endif
  554. func testHttpError() {
  555. let byName = functions.httpsCallable(
  556. "httpErrorTest",
  557. requestAs: [Int].self,
  558. responseAs: Int.self
  559. )
  560. let byURL = functions.httpsCallable(
  561. emulatorURL("httpErrorTest"),
  562. requestAs: [Int].self,
  563. responseAs: Int.self
  564. )
  565. for function in [byName, byURL] {
  566. let expectation = expectation(description: #function)
  567. XCTAssertNotNil(function)
  568. function.call([]) { result in
  569. do {
  570. _ = try result.get()
  571. } catch {
  572. let error = error as NSError
  573. XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
  574. expectation.fulfill()
  575. return
  576. }
  577. XCTFail("Failed to throw error for missing result")
  578. }
  579. waitForExpectations(timeout: 5)
  580. }
  581. }
  582. #if compiler(>=5.5.2) && canImport(_Concurrency)
  583. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  584. func testHttpErrorAsync() async {
  585. let byName = functions.httpsCallable(
  586. "httpErrorTest",
  587. requestAs: [Int].self,
  588. responseAs: Int.self
  589. )
  590. let byURL = functions.httpsCallable(
  591. emulatorURL("httpErrorTest"),
  592. requestAs: [Int].self,
  593. responseAs: Int.self
  594. )
  595. for function in [byName, byURL] {
  596. do {
  597. _ = try await function.call([])
  598. XCTAssertFalse(true, "Failed to throw error for missing result")
  599. } catch {
  600. let error = error as NSError
  601. XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
  602. }
  603. }
  604. }
  605. #endif
  606. func testThrowError() {
  607. let byName = functions.httpsCallable(
  608. "throwTest",
  609. requestAs: [Int].self,
  610. responseAs: Int.self
  611. )
  612. let byURL = functions.httpsCallable(
  613. emulatorURL("throwTest"),
  614. requestAs: [Int].self,
  615. responseAs: Int.self
  616. )
  617. for function in [byName, byURL] {
  618. let expectation = expectation(description: #function)
  619. XCTAssertNotNil(function)
  620. function.call([]) { result in
  621. do {
  622. _ = try result.get()
  623. } catch {
  624. let error = error as NSError
  625. XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
  626. XCTAssertEqual(error.localizedDescription, "Invalid test requested.")
  627. expectation.fulfill()
  628. return
  629. }
  630. XCTFail("Failed to throw error for missing result")
  631. }
  632. waitForExpectations(timeout: 5)
  633. }
  634. }
  635. #if compiler(>=5.5.2) && canImport(_Concurrency)
  636. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  637. func testThrowErrorAsync() async {
  638. let byName = functions.httpsCallable(
  639. "throwTest",
  640. requestAs: [Int].self,
  641. responseAs: Int.self
  642. )
  643. let byURL = functions.httpsCallable(
  644. emulatorURL("throwTest"),
  645. requestAs: [Int].self,
  646. responseAs: Int.self
  647. )
  648. for function in [byName, byURL] {
  649. do {
  650. _ = try await function.call([])
  651. XCTAssertFalse(true, "Failed to throw error for missing result")
  652. } catch {
  653. let error = error as NSError
  654. XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
  655. XCTAssertEqual(error.localizedDescription, "Invalid test requested.")
  656. }
  657. }
  658. }
  659. #endif
  660. func testTimeout() {
  661. let byName = functions.httpsCallable(
  662. "timeoutTest",
  663. requestAs: [Int].self,
  664. responseAs: Int.self
  665. )
  666. let byURL = functions.httpsCallable(
  667. emulatorURL("timeoutTest"),
  668. requestAs: [Int].self,
  669. responseAs: Int.self
  670. )
  671. for var function in [byName, byURL] {
  672. let expectation = expectation(description: #function)
  673. function.timeoutInterval = 0.05
  674. function.call([]) { result in
  675. do {
  676. _ = try result.get()
  677. } catch {
  678. let error = error as NSError
  679. XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
  680. XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
  681. XCTAssertNil(error.userInfo["details"])
  682. expectation.fulfill()
  683. return
  684. }
  685. XCTFail("Failed to throw error for missing result")
  686. }
  687. waitForExpectations(timeout: 5)
  688. }
  689. }
  690. #if compiler(>=5.5.2) && canImport(_Concurrency)
  691. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  692. func testTimeoutAsync() async {
  693. var byName = functions.httpsCallable(
  694. "timeoutTest",
  695. requestAs: [Int].self,
  696. responseAs: Int.self
  697. )
  698. byName.timeoutInterval = 0.05
  699. var byURL = functions.httpsCallable(
  700. emulatorURL("timeoutTest"),
  701. requestAs: [Int].self,
  702. responseAs: Int.self
  703. )
  704. byURL.timeoutInterval = 0.05
  705. for function in [byName, byURL] {
  706. do {
  707. _ = try await function.call([])
  708. XCTAssertFalse(true, "Failed to throw error for missing result")
  709. } catch {
  710. let error = error as NSError
  711. XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
  712. XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
  713. XCTAssertNil(error.userInfo["details"])
  714. }
  715. }
  716. }
  717. #endif
  718. func testCallAsFunction() {
  719. let data = DataTestRequest(
  720. bool: true,
  721. int: 2,
  722. long: 9_876_543_210,
  723. string: "four",
  724. array: [5, 6],
  725. null: nil
  726. )
  727. let byName = functions.httpsCallable("dataTest",
  728. requestAs: DataTestRequest.self,
  729. responseAs: DataTestResponse.self)
  730. let byURL = functions.httpsCallable(emulatorURL("dataTest"),
  731. requestAs: DataTestRequest.self,
  732. responseAs: DataTestResponse.self)
  733. for function in [byName, byURL] {
  734. let expectation = expectation(description: #function)
  735. function(data) { result in
  736. do {
  737. let response = try result.get()
  738. let expected = DataTestResponse(
  739. message: "stub response",
  740. long: 420,
  741. code: 42
  742. )
  743. XCTAssertEqual(response, expected)
  744. expectation.fulfill()
  745. } catch {
  746. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  747. }
  748. }
  749. waitForExpectations(timeout: 5)
  750. }
  751. }
  752. #if compiler(>=5.5.2) && canImport(_Concurrency)
  753. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  754. func testCallAsFunctionAsync() async throws {
  755. let data = DataTestRequest(
  756. bool: true,
  757. int: 2,
  758. long: 9_876_543_210,
  759. string: "four",
  760. array: [5, 6],
  761. null: nil
  762. )
  763. let byName = functions.httpsCallable("dataTest",
  764. requestAs: DataTestRequest.self,
  765. responseAs: DataTestResponse.self)
  766. let byURL = functions.httpsCallable(emulatorURL("dataTest"),
  767. requestAs: DataTestRequest.self,
  768. responseAs: DataTestResponse.self)
  769. for function in [byName, byURL] {
  770. let response = try await function(data)
  771. let expected = DataTestResponse(
  772. message: "stub response",
  773. long: 420,
  774. code: 42
  775. )
  776. XCTAssertEqual(response, expected)
  777. }
  778. }
  779. #endif
  780. func testInferredTypes() {
  781. let data = DataTestRequest(
  782. bool: true,
  783. int: 2,
  784. long: 9_876_543_210,
  785. string: "four",
  786. array: [5, 6],
  787. null: nil
  788. )
  789. let byName: Callable<DataTestRequest, DataTestResponse> = functions.httpsCallable("dataTest")
  790. let byURL: Callable<DataTestRequest, DataTestResponse> = functions
  791. .httpsCallable(emulatorURL("dataTest"))
  792. for function in [byName, byURL] {
  793. let expectation = expectation(description: #function)
  794. function(data) { result in
  795. do {
  796. let response = try result.get()
  797. let expected = DataTestResponse(
  798. message: "stub response",
  799. long: 420,
  800. code: 42
  801. )
  802. XCTAssertEqual(response, expected)
  803. expectation.fulfill()
  804. } catch {
  805. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  806. }
  807. }
  808. waitForExpectations(timeout: 5)
  809. }
  810. }
  811. #if compiler(>=5.5.2) && canImport(_Concurrency)
  812. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  813. func testInferredTyesAsync() async throws {
  814. let data = DataTestRequest(
  815. bool: true,
  816. int: 2,
  817. long: 9_876_543_210,
  818. string: "four",
  819. array: [5, 6],
  820. null: nil
  821. )
  822. let byName: Callable<DataTestRequest, DataTestResponse> = functions
  823. .httpsCallable("dataTest")
  824. let byURL: Callable<DataTestRequest, DataTestResponse> = functions
  825. .httpsCallable(emulatorURL("dataTest"))
  826. for function in [byName, byURL] {
  827. let response = try await function(data)
  828. let expected = DataTestResponse(
  829. message: "stub response",
  830. long: 420,
  831. code: 42
  832. )
  833. XCTAssertEqual(response, expected)
  834. }
  835. }
  836. #endif
  837. }
  838. private class AuthTokenProvider: AuthInterop {
  839. func getUserID() -> String? {
  840. return "fake user"
  841. }
  842. let token: String
  843. init(token: String) {
  844. self.token = token
  845. }
  846. func getToken(forcingRefresh: Bool, completion: (String?, Error?) -> Void) {
  847. completion(token, nil)
  848. }
  849. }
  850. private class MessagingTokenProvider: NSObject, MessagingInterop {
  851. var fcmToken: String? { return "fakeFCMToken" }
  852. }