IntegrationTests.swift 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347
  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 FirebaseAuthInterop
  16. @testable import FirebaseFunctions
  17. import FirebaseMessagingInterop
  18. import XCTest
  19. /// This file was initialized 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. /// - Important: These tests require the emulator. Run `./FirebaseFunctions/Backend/start.sh`
  60. class IntegrationTests: XCTestCase {
  61. let functions = Functions(projectID: "functions-integration-test",
  62. region: "us-central1",
  63. customDomain: nil,
  64. auth: nil,
  65. messaging: MessagingTokenProvider(),
  66. appCheck: nil)
  67. override func setUp() {
  68. super.setUp()
  69. functions.useEmulator(withHost: "localhost", port: 5005)
  70. }
  71. func emulatorURL(_ funcName: String) -> URL {
  72. return URL(string: "http://localhost:5005/functions-integration-test/us-central1/\(funcName)")!
  73. }
  74. @MainActor func testData() {
  75. let data = DataTestRequest(
  76. bool: true,
  77. int: 2,
  78. long: 9_876_543_210,
  79. string: "four",
  80. array: [5, 6],
  81. null: nil
  82. )
  83. let byName = functions.httpsCallable("dataTest",
  84. requestAs: DataTestRequest.self,
  85. responseAs: DataTestResponse.self)
  86. let byURL = functions.httpsCallable(emulatorURL("dataTest"),
  87. requestAs: DataTestRequest.self,
  88. responseAs: DataTestResponse.self)
  89. for function in [byName, byURL] {
  90. let expectation = expectation(description: #function)
  91. function.call(data) { result in
  92. do {
  93. let response = try result.get()
  94. let expected = DataTestResponse(
  95. message: "stub response",
  96. long: 420,
  97. code: 42
  98. )
  99. XCTAssertEqual(response, expected)
  100. } catch {
  101. XCTFail("Failed to unwrap the function result: \(error)")
  102. }
  103. expectation.fulfill()
  104. }
  105. waitForExpectations(timeout: 5)
  106. }
  107. }
  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. @MainActor func testScalar() {
  135. let byName = functions.httpsCallable(
  136. "scalarTest",
  137. requestAs: Int16.self,
  138. responseAs: Int.self
  139. )
  140. let byURL = functions.httpsCallable(
  141. emulatorURL("scalarTest"),
  142. requestAs: Int16.self,
  143. responseAs: Int.self
  144. )
  145. for function in [byName, byURL] {
  146. let expectation = expectation(description: #function)
  147. function.call(17) { result in
  148. do {
  149. let response = try result.get()
  150. XCTAssertEqual(response, 76)
  151. } catch {
  152. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  153. }
  154. expectation.fulfill()
  155. }
  156. waitForExpectations(timeout: 5)
  157. }
  158. }
  159. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  160. func testScalarAsync() async throws {
  161. let byName = functions.httpsCallable(
  162. "scalarTest",
  163. requestAs: Int16.self,
  164. responseAs: Int.self
  165. )
  166. let byURL = functions.httpsCallable(
  167. emulatorURL("scalarTest"),
  168. requestAs: Int16.self,
  169. responseAs: Int.self
  170. )
  171. for function in [byName, byURL] {
  172. let result = try await function.call(17)
  173. XCTAssertEqual(result, 76)
  174. }
  175. }
  176. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  177. func testScalarAsyncAlternateSignature() async throws {
  178. let byName: Callable<Int16, Int> = functions.httpsCallable("scalarTest")
  179. let byURL: Callable<Int16, Int> = functions.httpsCallable(emulatorURL("scalarTest"))
  180. for function in [byName, byURL] {
  181. let result = try await function.call(17)
  182. XCTAssertEqual(result, 76)
  183. }
  184. }
  185. @MainActor func testToken() {
  186. // Recreate functions with a token.
  187. let functions = Functions(
  188. projectID: "functions-integration-test",
  189. region: "us-central1",
  190. customDomain: nil,
  191. auth: AuthTokenProvider(token: "token"),
  192. messaging: MessagingTokenProvider(),
  193. appCheck: nil
  194. )
  195. functions.useEmulator(withHost: "localhost", port: 5005)
  196. let byName = functions.httpsCallable(
  197. "tokenTest",
  198. requestAs: [String: Int].self,
  199. responseAs: [String: Int].self
  200. )
  201. let byURL = functions.httpsCallable(
  202. emulatorURL("tokenTest"),
  203. requestAs: [String: Int].self,
  204. responseAs: [String: Int].self
  205. )
  206. for function in [byName, byURL] {
  207. let expectation = expectation(description: #function)
  208. XCTAssertNotNil(function)
  209. function.call([:]) { result in
  210. do {
  211. let data = try result.get()
  212. XCTAssertEqual(data, [:])
  213. } catch {
  214. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  215. }
  216. expectation.fulfill()
  217. }
  218. waitForExpectations(timeout: 5)
  219. }
  220. }
  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. @MainActor func testFCMToken() {
  249. let byName = functions.httpsCallable(
  250. "FCMTokenTest",
  251. requestAs: [String: Int].self,
  252. responseAs: [String: Int].self
  253. )
  254. let byURL = functions.httpsCallable(
  255. emulatorURL("FCMTokenTest"),
  256. requestAs: [String: Int].self,
  257. responseAs: [String: Int].self
  258. )
  259. for function in [byName, byURL] {
  260. let expectation = expectation(description: #function)
  261. function.call([:]) { result in
  262. do {
  263. let data = try result.get()
  264. XCTAssertEqual(data, [:])
  265. } catch {
  266. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  267. }
  268. expectation.fulfill()
  269. }
  270. waitForExpectations(timeout: 5)
  271. }
  272. }
  273. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  274. func testFCMTokenAsync() async throws {
  275. let byName = functions.httpsCallable(
  276. "FCMTokenTest",
  277. requestAs: [String: Int].self,
  278. responseAs: [String: Int].self
  279. )
  280. let byURL = functions.httpsCallable(
  281. emulatorURL("FCMTokenTest"),
  282. requestAs: [String: Int].self,
  283. responseAs: [String: Int].self
  284. )
  285. for function in [byName, byURL] {
  286. let data = try await function.call([:])
  287. XCTAssertEqual(data, [:])
  288. }
  289. }
  290. @MainActor func testNull() {
  291. let byName = functions.httpsCallable(
  292. "nullTest",
  293. requestAs: Int?.self,
  294. responseAs: Int?.self
  295. )
  296. let byURL = functions.httpsCallable(
  297. emulatorURL("nullTest"),
  298. requestAs: Int?.self,
  299. responseAs: Int?.self
  300. )
  301. for function in [byName, byURL] {
  302. let expectation = expectation(description: #function)
  303. function.call(nil) { result in
  304. do {
  305. let data = try result.get()
  306. XCTAssertEqual(data, nil)
  307. } catch {
  308. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  309. }
  310. expectation.fulfill()
  311. }
  312. waitForExpectations(timeout: 5)
  313. }
  314. }
  315. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  316. func testNullAsync() async throws {
  317. let byName = functions.httpsCallable(
  318. "nullTest",
  319. requestAs: Int?.self,
  320. responseAs: Int?.self
  321. )
  322. let byURL = functions.httpsCallable(
  323. emulatorURL("nullTest"),
  324. requestAs: Int?.self,
  325. responseAs: Int?.self
  326. )
  327. for function in [byName, byURL] {
  328. let data = try await function.call(nil)
  329. XCTAssertEqual(data, nil)
  330. }
  331. }
  332. @MainActor func testMissingResult() {
  333. let byName = functions.httpsCallable(
  334. "missingResultTest",
  335. requestAs: Int?.self,
  336. responseAs: Int?.self
  337. )
  338. let byURL = functions.httpsCallable(
  339. emulatorURL("missingResultTest"),
  340. requestAs: Int?.self,
  341. responseAs: Int?.self
  342. )
  343. for function in [byName, byURL] {
  344. let expectation = expectation(description: #function)
  345. function.call(nil) { result in
  346. do {
  347. _ = try result.get()
  348. } catch {
  349. let error = error as NSError
  350. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  351. XCTAssertEqual("Response is missing data field.", error.localizedDescription)
  352. expectation.fulfill()
  353. return
  354. }
  355. XCTFail("Failed to throw error for missing result")
  356. }
  357. waitForExpectations(timeout: 5)
  358. }
  359. }
  360. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  361. func testMissingResultAsync() async {
  362. let byName = functions.httpsCallable(
  363. "missingResultTest",
  364. requestAs: Int?.self,
  365. responseAs: Int?.self
  366. )
  367. let byURL = functions.httpsCallable(
  368. emulatorURL("missingResultTest"),
  369. requestAs: Int?.self,
  370. responseAs: Int?.self
  371. )
  372. for function in [byName, byURL] {
  373. do {
  374. _ = try await function.call(nil)
  375. XCTFail("Failed to throw error for missing result")
  376. } catch {
  377. let error = error as NSError
  378. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  379. XCTAssertEqual("Response is missing data field.", error.localizedDescription)
  380. }
  381. }
  382. }
  383. @MainActor func testUnhandledError() {
  384. let byName = functions.httpsCallable(
  385. "unhandledErrorTest",
  386. requestAs: [Int].self,
  387. responseAs: Int.self
  388. )
  389. let byURL = functions.httpsCallable(
  390. emulatorURL("unhandledErrorTest"),
  391. requestAs: [Int].self,
  392. responseAs: Int.self
  393. )
  394. for function in [byName, byURL] {
  395. let expectation = expectation(description: #function)
  396. function.call([]) { result in
  397. do {
  398. _ = try result.get()
  399. } catch {
  400. let error = error as NSError
  401. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  402. XCTAssertEqual("INTERNAL", error.localizedDescription)
  403. expectation.fulfill()
  404. return
  405. }
  406. XCTFail("Failed to throw error for missing result")
  407. }
  408. XCTAssert(true)
  409. waitForExpectations(timeout: 5)
  410. }
  411. }
  412. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  413. func testUnhandledErrorAsync() async {
  414. let byName = functions.httpsCallable(
  415. "unhandledErrorTest",
  416. requestAs: [Int].self,
  417. responseAs: Int.self
  418. )
  419. let byURL = functions.httpsCallable(
  420. "unhandledErrorTest",
  421. requestAs: [Int].self,
  422. responseAs: Int.self
  423. )
  424. for function in [byName, byURL] {
  425. do {
  426. _ = try await function.call([])
  427. XCTFail("Failed to throw error for missing result")
  428. } catch {
  429. let error = error as NSError
  430. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  431. XCTAssertEqual("INTERNAL", error.localizedDescription)
  432. }
  433. }
  434. }
  435. @MainActor func testUnknownError() {
  436. let byName = functions.httpsCallable(
  437. "unknownErrorTest",
  438. requestAs: [Int].self,
  439. responseAs: Int.self
  440. )
  441. let byURL = functions.httpsCallable(
  442. emulatorURL("unknownErrorTest"),
  443. requestAs: [Int].self,
  444. responseAs: Int.self
  445. )
  446. for function in [byName, byURL] {
  447. let expectation = expectation(description: #function)
  448. function.call([]) { result in
  449. do {
  450. _ = try result.get()
  451. } catch {
  452. let error = error as NSError
  453. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  454. XCTAssertEqual("INTERNAL", error.localizedDescription)
  455. expectation.fulfill()
  456. return
  457. }
  458. XCTFail("Failed to throw error for missing result")
  459. }
  460. }
  461. waitForExpectations(timeout: 5)
  462. }
  463. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  464. func testUnknownErrorAsync() async {
  465. let byName = functions.httpsCallable(
  466. "unknownErrorTest",
  467. requestAs: [Int].self,
  468. responseAs: Int.self
  469. )
  470. let byURL = functions.httpsCallable(
  471. emulatorURL("unknownErrorTest"),
  472. requestAs: [Int].self,
  473. responseAs: Int.self
  474. )
  475. for function in [byName, byURL] {
  476. do {
  477. _ = try await function.call([])
  478. XCTAssertFalse(true, "Failed to throw error for missing result")
  479. } catch {
  480. let error = error as NSError
  481. XCTAssertEqual(FunctionsErrorCode.internal.rawValue, error.code)
  482. XCTAssertEqual("INTERNAL", error.localizedDescription)
  483. }
  484. }
  485. }
  486. @MainActor func testExplicitError() {
  487. let byName = functions.httpsCallable(
  488. "explicitErrorTest",
  489. requestAs: [Int].self,
  490. responseAs: Int.self
  491. )
  492. let byURL = functions.httpsCallable(
  493. "explicitErrorTest",
  494. requestAs: [Int].self,
  495. responseAs: Int.self
  496. )
  497. for function in [byName, byURL] {
  498. let expectation = expectation(description: #function)
  499. function.call([]) { result in
  500. do {
  501. _ = try result.get()
  502. } catch {
  503. let error = error as NSError
  504. XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
  505. XCTAssertEqual("explicit nope", error.localizedDescription)
  506. XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
  507. error.userInfo["details"] as? [String: Int32])
  508. expectation.fulfill()
  509. return
  510. }
  511. XCTFail("Failed to throw error for missing result")
  512. }
  513. waitForExpectations(timeout: 5)
  514. }
  515. }
  516. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  517. func testExplicitErrorAsync() async {
  518. let byName = functions.httpsCallable(
  519. "explicitErrorTest",
  520. requestAs: [Int].self,
  521. responseAs: Int.self
  522. )
  523. let byURL = functions.httpsCallable(
  524. emulatorURL("explicitErrorTest"),
  525. requestAs: [Int].self,
  526. responseAs: Int.self
  527. )
  528. for function in [byName, byURL] {
  529. do {
  530. _ = try await function.call([])
  531. XCTAssertFalse(true, "Failed to throw error for missing result")
  532. } catch {
  533. let error = error as NSError
  534. XCTAssertEqual(FunctionsErrorCode.outOfRange.rawValue, error.code)
  535. XCTAssertEqual("explicit nope", error.localizedDescription)
  536. XCTAssertEqual(["start": 10 as Int32, "end": 20 as Int32, "long": 30],
  537. error.userInfo["details"] as? [String: Int32])
  538. }
  539. }
  540. }
  541. @MainActor func testHttpError() {
  542. let byName = functions.httpsCallable(
  543. "httpErrorTest",
  544. requestAs: [Int].self,
  545. responseAs: Int.self
  546. )
  547. let byURL = functions.httpsCallable(
  548. emulatorURL("httpErrorTest"),
  549. requestAs: [Int].self,
  550. responseAs: Int.self
  551. )
  552. for function in [byName, byURL] {
  553. let expectation = expectation(description: #function)
  554. XCTAssertNotNil(function)
  555. function.call([]) { result in
  556. do {
  557. _ = try result.get()
  558. } catch {
  559. let error = error as NSError
  560. XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
  561. expectation.fulfill()
  562. return
  563. }
  564. XCTFail("Failed to throw error for missing result")
  565. }
  566. waitForExpectations(timeout: 5)
  567. }
  568. }
  569. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  570. func testHttpErrorAsync() async {
  571. let byName = functions.httpsCallable(
  572. "httpErrorTest",
  573. requestAs: [Int].self,
  574. responseAs: Int.self
  575. )
  576. let byURL = functions.httpsCallable(
  577. emulatorURL("httpErrorTest"),
  578. requestAs: [Int].self,
  579. responseAs: Int.self
  580. )
  581. for function in [byName, byURL] {
  582. do {
  583. _ = try await function.call([])
  584. XCTAssertFalse(true, "Failed to throw error for missing result")
  585. } catch {
  586. let error = error as NSError
  587. XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
  588. }
  589. }
  590. }
  591. @MainActor func testThrowError() {
  592. let byName = functions.httpsCallable(
  593. "throwTest",
  594. requestAs: [Int].self,
  595. responseAs: Int.self
  596. )
  597. let byURL = functions.httpsCallable(
  598. emulatorURL("throwTest"),
  599. requestAs: [Int].self,
  600. responseAs: Int.self
  601. )
  602. for function in [byName, byURL] {
  603. let expectation = expectation(description: #function)
  604. XCTAssertNotNil(function)
  605. function.call([]) { result in
  606. do {
  607. _ = try result.get()
  608. } catch {
  609. let error = error as NSError
  610. XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
  611. XCTAssertEqual(error.localizedDescription, "Invalid test requested.")
  612. expectation.fulfill()
  613. return
  614. }
  615. XCTFail("Failed to throw error for missing result")
  616. }
  617. waitForExpectations(timeout: 5)
  618. }
  619. }
  620. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  621. func testThrowErrorAsync() async {
  622. let byName = functions.httpsCallable(
  623. "throwTest",
  624. requestAs: [Int].self,
  625. responseAs: Int.self
  626. )
  627. let byURL = functions.httpsCallable(
  628. emulatorURL("throwTest"),
  629. requestAs: [Int].self,
  630. responseAs: Int.self
  631. )
  632. for function in [byName, byURL] {
  633. do {
  634. _ = try await function.call([])
  635. XCTAssertFalse(true, "Failed to throw error for missing result")
  636. } catch {
  637. let error = error as NSError
  638. XCTAssertEqual(FunctionsErrorCode.invalidArgument.rawValue, error.code)
  639. XCTAssertEqual(error.localizedDescription, "Invalid test requested.")
  640. }
  641. }
  642. }
  643. @MainActor func testTimeout() {
  644. let byName = functions.httpsCallable(
  645. "timeoutTest",
  646. requestAs: [Int].self,
  647. responseAs: Int.self
  648. )
  649. let byURL = functions.httpsCallable(
  650. emulatorURL("timeoutTest"),
  651. requestAs: [Int].self,
  652. responseAs: Int.self
  653. )
  654. for var function in [byName, byURL] {
  655. let expectation = expectation(description: #function)
  656. function.timeoutInterval = 0.05
  657. function.call([]) { result in
  658. do {
  659. _ = try result.get()
  660. } catch {
  661. let error = error as NSError
  662. XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
  663. XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
  664. XCTAssertNil(error.userInfo["details"])
  665. expectation.fulfill()
  666. return
  667. }
  668. XCTFail("Failed to throw error for missing result")
  669. }
  670. waitForExpectations(timeout: 5)
  671. }
  672. }
  673. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  674. func testTimeoutAsync() async {
  675. var byName = functions.httpsCallable(
  676. "timeoutTest",
  677. requestAs: [Int].self,
  678. responseAs: Int.self
  679. )
  680. byName.timeoutInterval = 0.05
  681. var byURL = functions.httpsCallable(
  682. emulatorURL("timeoutTest"),
  683. requestAs: [Int].self,
  684. responseAs: Int.self
  685. )
  686. byURL.timeoutInterval = 0.05
  687. for function in [byName, byURL] {
  688. do {
  689. _ = try await function.call([])
  690. XCTAssertFalse(true, "Failed to throw error for missing result")
  691. } catch {
  692. let error = error as NSError
  693. XCTAssertEqual(FunctionsErrorCode.deadlineExceeded.rawValue, error.code)
  694. XCTAssertEqual("DEADLINE EXCEEDED", error.localizedDescription)
  695. XCTAssertNil(error.userInfo["details"])
  696. }
  697. }
  698. }
  699. @MainActor func testCallAsFunction() {
  700. let data = DataTestRequest(
  701. bool: true,
  702. int: 2,
  703. long: 9_876_543_210,
  704. string: "four",
  705. array: [5, 6],
  706. null: nil
  707. )
  708. let byName = functions.httpsCallable("dataTest",
  709. requestAs: DataTestRequest.self,
  710. responseAs: DataTestResponse.self)
  711. let byURL = functions.httpsCallable(emulatorURL("dataTest"),
  712. requestAs: DataTestRequest.self,
  713. responseAs: DataTestResponse.self)
  714. for function in [byName, byURL] {
  715. let expectation = expectation(description: #function)
  716. function(data) { result in
  717. do {
  718. let response = try result.get()
  719. let expected = DataTestResponse(
  720. message: "stub response",
  721. long: 420,
  722. code: 42
  723. )
  724. XCTAssertEqual(response, expected)
  725. expectation.fulfill()
  726. } catch {
  727. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  728. }
  729. }
  730. waitForExpectations(timeout: 5)
  731. }
  732. }
  733. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  734. func testCallAsFunctionAsync() async throws {
  735. let data = DataTestRequest(
  736. bool: true,
  737. int: 2,
  738. long: 9_876_543_210,
  739. string: "four",
  740. array: [5, 6],
  741. null: nil
  742. )
  743. let byName = functions.httpsCallable("dataTest",
  744. requestAs: DataTestRequest.self,
  745. responseAs: DataTestResponse.self)
  746. let byURL = functions.httpsCallable(emulatorURL("dataTest"),
  747. requestAs: DataTestRequest.self,
  748. responseAs: DataTestResponse.self)
  749. for function in [byName, byURL] {
  750. let response = try await function(data)
  751. let expected = DataTestResponse(
  752. message: "stub response",
  753. long: 420,
  754. code: 42
  755. )
  756. XCTAssertEqual(response, expected)
  757. }
  758. }
  759. @MainActor func testInferredTypes() {
  760. let data = DataTestRequest(
  761. bool: true,
  762. int: 2,
  763. long: 9_876_543_210,
  764. string: "four",
  765. array: [5, 6],
  766. null: nil
  767. )
  768. let byName: Callable<DataTestRequest, DataTestResponse> = functions.httpsCallable("dataTest")
  769. let byURL: Callable<DataTestRequest, DataTestResponse> = functions
  770. .httpsCallable(emulatorURL("dataTest"))
  771. for function in [byName, byURL] {
  772. let expectation = expectation(description: #function)
  773. function(data) { result in
  774. do {
  775. let response = try result.get()
  776. let expected = DataTestResponse(
  777. message: "stub response",
  778. long: 420,
  779. code: 42
  780. )
  781. XCTAssertEqual(response, expected)
  782. expectation.fulfill()
  783. } catch {
  784. XCTAssert(false, "Failed to unwrap the function result: \(error)")
  785. }
  786. }
  787. waitForExpectations(timeout: 5)
  788. }
  789. }
  790. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  791. func testInferredTyesAsync() async throws {
  792. let data = DataTestRequest(
  793. bool: true,
  794. int: 2,
  795. long: 9_876_543_210,
  796. string: "four",
  797. array: [5, 6],
  798. null: nil
  799. )
  800. let byName: Callable<DataTestRequest, DataTestResponse> = functions
  801. .httpsCallable("dataTest")
  802. let byURL: Callable<DataTestRequest, DataTestResponse> = functions
  803. .httpsCallable(emulatorURL("dataTest"))
  804. for function in [byName, byURL] {
  805. let response = try await function(data)
  806. let expected = DataTestResponse(
  807. message: "stub response",
  808. long: 420,
  809. code: 42
  810. )
  811. XCTAssertEqual(response, expected)
  812. }
  813. }
  814. @MainActor func testFunctionsReturnsOnMainThread() {
  815. let expectation = expectation(description: #function)
  816. functions.httpsCallable(
  817. "scalarTest",
  818. requestAs: Int16.self,
  819. responseAs: Int.self
  820. ).call(17) { result in
  821. guard case .success = result else {
  822. return XCTFail("Unexpected failure.")
  823. }
  824. XCTAssert(Thread.isMainThread)
  825. expectation.fulfill()
  826. }
  827. waitForExpectations(timeout: 5)
  828. }
  829. @MainActor func testFunctionsThrowsOnMainThread() {
  830. let expectation = expectation(description: #function)
  831. functions.httpsCallable(
  832. "httpErrorTest",
  833. requestAs: [Int].self,
  834. responseAs: Int.self
  835. ).call([]) { result in
  836. guard case .failure = result else {
  837. return XCTFail("Unexpected failure.")
  838. }
  839. XCTAssert(Thread.isMainThread)
  840. expectation.fulfill()
  841. }
  842. waitForExpectations(timeout: 5)
  843. }
  844. }
  845. // MARK: - Streaming
  846. /// A convenience type used to represent that a callable function does not
  847. /// accept parameters.
  848. ///
  849. /// This can be used as the generic `Request` parameter to ``Callable`` to
  850. /// indicate the callable function does not accept parameters.
  851. private struct EmptyRequest: Encodable, Sendable {}
  852. @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
  853. extension IntegrationTests {
  854. func testStream_NoArgs() async throws {
  855. // 1. Custom `EmptyRequest` struct is passed as a placeholder generic arg.
  856. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStream")
  857. // 2. No request data is passed when creating stream.
  858. let stream = try callable.stream()
  859. var streamContents: [String] = []
  860. for try await response in stream {
  861. streamContents.append(response)
  862. }
  863. XCTAssertEqual(
  864. streamContents,
  865. ["hello", "world", "this", "is", "cool"]
  866. )
  867. }
  868. @available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
  869. func testStream_NoArgs_UeeNever() async throws {
  870. let callable: Callable<Never, String> = functions.httpsCallable("genStream")
  871. let stream = try callable.stream()
  872. var streamContents: [String] = []
  873. for try await response in stream {
  874. streamContents.append(response)
  875. }
  876. XCTAssertEqual(
  877. streamContents,
  878. ["hello", "world", "this", "is", "cool"]
  879. )
  880. }
  881. func testStream_SimpleStreamResponse() async throws {
  882. let callable: Callable<EmptyRequest, StreamResponse<String, String>> = functions
  883. .httpsCallable("genStream")
  884. let stream = try callable.stream()
  885. var streamContents: [String] = []
  886. for try await response in stream {
  887. switch response {
  888. case let .message(message):
  889. streamContents.append(message)
  890. case let .result(result):
  891. streamContents.append(result)
  892. }
  893. }
  894. XCTAssertEqual(
  895. streamContents,
  896. ["hello", "world", "this", "is", "cool", "hello world this is cool"]
  897. )
  898. }
  899. func testStream_CodableString() async throws {
  900. let byName: Callable<EmptyRequest, String> = functions.httpsCallable("genStream")
  901. let stream = try byName.stream()
  902. let result: [String] = try await stream.reduce([]) { $0 + [$1] }
  903. XCTAssertEqual(result, ["hello", "world", "this", "is", "cool"])
  904. }
  905. private struct Location: Codable, Equatable {
  906. let name: String
  907. }
  908. private struct WeatherForecast: Decodable, Equatable {
  909. enum Conditions: String, Decodable {
  910. case sunny
  911. case rainy
  912. case snowy
  913. }
  914. let location: Location
  915. let temperature: Int
  916. let conditions: Conditions
  917. }
  918. private struct WeatherForecastReport: Decodable, Equatable {
  919. let forecasts: [WeatherForecast]
  920. }
  921. func testStream_CodableObject() async throws {
  922. let callable: Callable<[Location], WeatherForecast> = functions
  923. .httpsCallable("genStreamWeather")
  924. let stream = try callable.stream([
  925. Location(name: "Toronto"),
  926. Location(name: "London"),
  927. Location(name: "Dubai"),
  928. ])
  929. let result: [WeatherForecast] = try await stream.reduce([]) { $0 + [$1] }
  930. XCTAssertEqual(
  931. result,
  932. [
  933. WeatherForecast(location: Location(name: "Toronto"), temperature: 25, conditions: .snowy),
  934. WeatherForecast(location: Location(name: "London"), temperature: 50, conditions: .rainy),
  935. WeatherForecast(location: Location(name: "Dubai"), temperature: 75, conditions: .sunny),
  936. ]
  937. )
  938. }
  939. func testStream_ResponseMessageDecodingFailure() async throws {
  940. let callable: Callable<[Location], StreamResponse<WeatherForecast, WeatherForecastReport>> =
  941. functions
  942. .httpsCallable("genStreamWeatherError")
  943. let stream = try callable.stream([Location(name: "Toronto")])
  944. do {
  945. for try await _ in stream {
  946. XCTFail("Expected error to be thrown from stream.")
  947. }
  948. } catch let error as FunctionsError where error.code == .dataLoss {
  949. XCTAssertNotNil(error.errorUserInfo[NSUnderlyingErrorKey] as? DecodingError)
  950. }
  951. }
  952. func testStream_ResponseResultDecodingFailure() async throws {
  953. let callable: Callable<[Location], StreamResponse<WeatherForecast, String>> = functions
  954. .httpsCallable("genStreamWeather")
  955. let stream = try callable.stream([Location(name: "Toronto")])
  956. do {
  957. for try await response in stream {
  958. if case .result = response {
  959. XCTFail("Expected error to be thrown from stream.")
  960. }
  961. }
  962. } catch let error as FunctionsError where error.code == .dataLoss {
  963. XCTAssertNotNil(error.errorUserInfo[NSUnderlyingErrorKey] as? DecodingError)
  964. }
  965. }
  966. func testStream_ComplexStreamResponse() async throws {
  967. let callable: Callable<[Location], StreamResponse<WeatherForecast, WeatherForecastReport>> =
  968. functions
  969. .httpsCallable("genStreamWeather")
  970. let stream = try callable.stream([
  971. Location(name: "Toronto"),
  972. Location(name: "London"),
  973. Location(name: "Dubai"),
  974. ])
  975. var streamContents: [WeatherForecast] = []
  976. var streamResult: WeatherForecastReport?
  977. for try await response in stream {
  978. switch response {
  979. case let .message(message):
  980. streamContents.append(message)
  981. case let .result(result):
  982. streamResult = result
  983. }
  984. }
  985. XCTAssertEqual(
  986. streamContents,
  987. [
  988. WeatherForecast(location: Location(name: "Toronto"), temperature: 25, conditions: .snowy),
  989. WeatherForecast(location: Location(name: "London"), temperature: 50, conditions: .rainy),
  990. WeatherForecast(location: Location(name: "Dubai"), temperature: 75, conditions: .sunny),
  991. ]
  992. )
  993. try XCTAssertEqual(
  994. XCTUnwrap(streamResult), WeatherForecastReport(forecasts: streamContents)
  995. )
  996. }
  997. func testStream_ComplexStreamResponse_Functional() async throws {
  998. let callable: Callable<[Location], StreamResponse<WeatherForecast, WeatherForecastReport>> =
  999. functions
  1000. .httpsCallable("genStreamWeather")
  1001. let stream = try callable.stream([
  1002. Location(name: "Toronto"),
  1003. Location(name: "London"),
  1004. Location(name: "Dubai"),
  1005. ])
  1006. let result: (accumulatedMessages: [WeatherForecast], result: WeatherForecastReport?) =
  1007. try await stream.reduce(([], nil)) { partialResult, streamResponse in
  1008. switch streamResponse {
  1009. case let .message(message):
  1010. (partialResult.accumulatedMessages + [message], partialResult.result)
  1011. case let .result(result):
  1012. (partialResult.accumulatedMessages, result)
  1013. }
  1014. }
  1015. XCTAssertEqual(
  1016. result.accumulatedMessages,
  1017. [
  1018. WeatherForecast(location: Location(name: "Toronto"), temperature: 25, conditions: .snowy),
  1019. WeatherForecast(location: Location(name: "London"), temperature: 50, conditions: .rainy),
  1020. WeatherForecast(location: Location(name: "Dubai"), temperature: 75, conditions: .sunny),
  1021. ]
  1022. )
  1023. try XCTAssertEqual(
  1024. XCTUnwrap(result.result), WeatherForecastReport(forecasts: result.accumulatedMessages)
  1025. )
  1026. }
  1027. // Concurrency rules prevent easily testing this feature.
  1028. #if swift(<6)
  1029. func testStream_Canceled() async throws {
  1030. let task = Task.detached { [self] in
  1031. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStream")
  1032. let stream = try callable.stream()
  1033. // Since we cancel the call we are expecting an empty array.
  1034. return try await stream.reduce([]) { $0 + [$1] } as [String]
  1035. }
  1036. // We cancel the task and we expect a null response even if the stream was initiated.
  1037. task.cancel()
  1038. let respone = try await task.value
  1039. XCTAssertEqual(respone, [])
  1040. }
  1041. #endif
  1042. func testStream_NonexistentFunction() async throws {
  1043. let callable: Callable<EmptyRequest, String> = functions.httpsCallable(
  1044. "nonexistentFunction"
  1045. )
  1046. let stream = try callable.stream()
  1047. do {
  1048. for try await _ in stream {
  1049. XCTFail("Expected error to be thrown from stream.")
  1050. }
  1051. } catch let error as FunctionsError where error.code == .notFound {
  1052. XCTAssertEqual(error.localizedDescription, "NOT FOUND")
  1053. }
  1054. }
  1055. func testStream_StreamError() async throws {
  1056. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStreamError")
  1057. let stream = try callable.stream()
  1058. do {
  1059. for try await _ in stream {
  1060. XCTFail("Expected error to be thrown from stream.")
  1061. }
  1062. } catch let error as FunctionsError where error.code == .internal {
  1063. XCTAssertEqual(error.localizedDescription, "INTERNAL")
  1064. }
  1065. }
  1066. func testStream_RequestEncodingFailure() async throws {
  1067. struct Foo: Encodable {
  1068. enum CodingKeys: CodingKey {}
  1069. func encode(to encoder: any Encoder) throws {
  1070. throw EncodingError
  1071. .invalidValue("", EncodingError.Context(codingPath: [], debugDescription: ""))
  1072. }
  1073. }
  1074. let callable: Callable<Foo, String> = functions
  1075. .httpsCallable("genStream")
  1076. do {
  1077. _ = try callable.stream(Foo())
  1078. } catch let error as FunctionsError where error.code == .invalidArgument {
  1079. _ = try XCTUnwrap(error.errorUserInfo[NSUnderlyingErrorKey] as? EncodingError)
  1080. }
  1081. }
  1082. /// This tests an edge case to assert that if a custom `Response` is used
  1083. /// that matches the decoding logic of `StreamResponse`, the custom
  1084. /// `Response` does not decode successfully.
  1085. func testStream_ResultIsOnlyExposedInStreamResponse() async throws {
  1086. // The implementation is copied from `StreamResponse`. The only difference is the do-catch is
  1087. // removed from the decoding initializer.
  1088. enum MyStreamResponse<Message: Decodable & Sendable, Result: Decodable & Sendable>: Decodable,
  1089. Sendable {
  1090. /// The message yielded by the callable function.
  1091. case message(Message)
  1092. /// The final result returned by the callable function.
  1093. case result(Result)
  1094. private enum CodingKeys: String, CodingKey {
  1095. case message
  1096. case result
  1097. }
  1098. public init(from decoder: any Decoder) throws {
  1099. let container = try decoder
  1100. .container(keyedBy: Self<Message, Result>.CodingKeys.self)
  1101. var allKeys = ArraySlice(container.allKeys)
  1102. guard let onlyKey = allKeys.popFirst(), allKeys.isEmpty else {
  1103. throw DecodingError
  1104. .typeMismatch(
  1105. Self<Message,
  1106. Result>.self,
  1107. DecodingError.Context(
  1108. codingPath: container.codingPath,
  1109. debugDescription: "Invalid number of keys found, expected one.",
  1110. underlyingError: nil
  1111. )
  1112. )
  1113. }
  1114. switch onlyKey {
  1115. case .message:
  1116. self = try Self
  1117. .message(container.decode(Message.self, forKey: .message))
  1118. case .result:
  1119. self = try Self
  1120. .result(container.decode(Result.self, forKey: .result))
  1121. }
  1122. }
  1123. }
  1124. let callable: Callable<[Location], MyStreamResponse<WeatherForecast, WeatherForecastReport>> =
  1125. functions
  1126. .httpsCallable("genStreamWeather")
  1127. let stream = try callable.stream([Location(name: "Toronto")])
  1128. do {
  1129. for try await _ in stream {
  1130. XCTFail("Expected error to be thrown from stream.")
  1131. }
  1132. } catch let error as FunctionsError where error.code == .dataLoss {
  1133. XCTAssertNotNil(error.errorUserInfo[NSUnderlyingErrorKey] as? DecodingError)
  1134. }
  1135. }
  1136. func testStream_ForNonStreamingCF3() async throws {
  1137. let callable: Callable<Int16, Int> = functions.httpsCallable("scalarTest")
  1138. let stream = try callable.stream(17)
  1139. do {
  1140. for try await _ in stream {
  1141. XCTFail("Expected error to be thrown from stream.")
  1142. }
  1143. } catch let error as FunctionsError where error.code == .dataLoss {
  1144. XCTAssertEqual(error.localizedDescription, "Unexpected format for streamed response.")
  1145. }
  1146. }
  1147. func testStream_EmptyStream() async throws {
  1148. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStreamEmpty")
  1149. var streamContents: [String] = []
  1150. for try await response in try callable.stream() {
  1151. streamContents.append(response)
  1152. }
  1153. XCTAssertEqual(streamContents, [])
  1154. }
  1155. func testStream_ResultOnly() async throws {
  1156. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStreamResultOnly")
  1157. let stream = try callable.stream()
  1158. for try await _ in stream {
  1159. // The stream should not yield anything, so this should not be reached.
  1160. XCTFail("Stream should not yield any messages")
  1161. }
  1162. // Because StreamResponse was not used, the result is not accessible,
  1163. // but the message should not throw.
  1164. }
  1165. func testStream_ResultOnly_StreamResponse() async throws {
  1166. struct EmptyResponse: Decodable, Sendable {}
  1167. let callable: Callable<EmptyRequest, StreamResponse<EmptyResponse, String>> = functions
  1168. .httpsCallable(
  1169. "genStreamResultOnly"
  1170. )
  1171. let stream = try callable.stream()
  1172. var streamResult = ""
  1173. for try await response in stream {
  1174. switch response {
  1175. case .message:
  1176. XCTFail("Stream should not yield any messages")
  1177. case let .result(result):
  1178. streamResult = result
  1179. }
  1180. }
  1181. // The hardcoded string matches the CF3's return value.
  1182. XCTAssertEqual(streamResult, "Only a result")
  1183. }
  1184. func testStream_UnexpectedType() async throws {
  1185. // This function yields strings, not integers.
  1186. let callable: Callable<EmptyRequest, Int> = functions.httpsCallable("genStream")
  1187. let stream = try callable.stream()
  1188. do {
  1189. for try await _ in stream {
  1190. XCTFail("Expected error to be thrown from stream.")
  1191. }
  1192. } catch let error as FunctionsError where error.code == .dataLoss {
  1193. XCTAssertNotNil(error.errorUserInfo[NSUnderlyingErrorKey] as? DecodingError)
  1194. }
  1195. }
  1196. func testStream_Timeout() async throws {
  1197. var callable: Callable<EmptyRequest, String> = functions.httpsCallable("timeoutTest")
  1198. // Set a short timeout
  1199. callable.timeoutInterval = 0.01 // 10 milliseconds
  1200. let stream = try callable.stream()
  1201. do {
  1202. for try await _ in stream {
  1203. XCTFail("Expected error to be thrown from stream.")
  1204. }
  1205. } catch let error as FunctionsError where error.code == .unavailable {
  1206. // This should be a timeout error.
  1207. XCTAssertEqual(
  1208. error.localizedDescription,
  1209. "The operation couldn’t be completed. (com.firebase.functions error 14.)"
  1210. )
  1211. XCTAssertNotNil(error.errorUserInfo[NSUnderlyingErrorKey] as? URLError)
  1212. }
  1213. }
  1214. func testStream_LargeData() async throws {
  1215. func generateLargeString() -> String {
  1216. var largeString = ""
  1217. for _ in 0 ..< 10000 {
  1218. largeString += "A"
  1219. }
  1220. return largeString
  1221. }
  1222. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStreamLargeData")
  1223. let stream = try callable.stream()
  1224. var concatenatedData = ""
  1225. for try await response in stream {
  1226. concatenatedData += response
  1227. }
  1228. // Assert that the concatenated data matches the expected large data.
  1229. XCTAssertEqual(concatenatedData, generateLargeString())
  1230. }
  1231. }
  1232. // MARK: - Helpers
  1233. private class AuthTokenProvider: AuthInterop {
  1234. func getUserID() -> String? {
  1235. return "fake user"
  1236. }
  1237. let token: String
  1238. init(token: String) {
  1239. self.token = token
  1240. }
  1241. func getToken(forcingRefresh: Bool, completion: (String?, Error?) -> Void) {
  1242. completion(token, nil)
  1243. }
  1244. }
  1245. private class MessagingTokenProvider: NSObject, MessagingInterop {
  1246. var fcmToken: String? { return "fakeFCMToken" }
  1247. }