IntegrationTests.swift 28 KB

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