IntegrationTests.swift 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343
  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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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 {}
  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. func testStream_Canceled() async throws {
  1028. let task = Task.detached { [self] in
  1029. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStream")
  1030. let stream = try callable.stream()
  1031. // Since we cancel the call we are expecting an empty array.
  1032. return try await stream.reduce([]) { $0 + [$1] } as [String]
  1033. }
  1034. // We cancel the task and we expect a null response even if the stream was initiated.
  1035. task.cancel()
  1036. let respone = try await task.value
  1037. XCTAssertEqual(respone, [])
  1038. }
  1039. func testStream_NonexistentFunction() async throws {
  1040. let callable: Callable<EmptyRequest, String> = functions.httpsCallable(
  1041. "nonexistentFunction"
  1042. )
  1043. let stream = try callable.stream()
  1044. do {
  1045. for try await _ in stream {
  1046. XCTFail("Expected error to be thrown from stream.")
  1047. }
  1048. } catch let error as FunctionsError where error.code == .notFound {
  1049. XCTAssertEqual(error.localizedDescription, "NOT FOUND")
  1050. }
  1051. }
  1052. func testStream_StreamError() async throws {
  1053. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStreamError")
  1054. let stream = try callable.stream()
  1055. do {
  1056. for try await _ in stream {
  1057. XCTFail("Expected error to be thrown from stream.")
  1058. }
  1059. } catch let error as FunctionsError where error.code == .internal {
  1060. XCTAssertEqual(error.localizedDescription, "INTERNAL")
  1061. }
  1062. }
  1063. func testStream_RequestEncodingFailure() async throws {
  1064. struct Foo: Encodable {
  1065. enum CodingKeys: CodingKey {}
  1066. func encode(to encoder: any Encoder) throws {
  1067. throw EncodingError
  1068. .invalidValue("", EncodingError.Context(codingPath: [], debugDescription: ""))
  1069. }
  1070. }
  1071. let callable: Callable<Foo, String> = functions
  1072. .httpsCallable("genStream")
  1073. do {
  1074. _ = try callable.stream(Foo())
  1075. } catch let error as FunctionsError where error.code == .invalidArgument {
  1076. _ = try XCTUnwrap(error.errorUserInfo[NSUnderlyingErrorKey] as? EncodingError)
  1077. }
  1078. }
  1079. /// This tests an edge case to assert that if a custom `Response` is used
  1080. /// that matches the decoding logic of `StreamResponse`, the custom
  1081. /// `Response` does not decode successfully.
  1082. func testStream_ResultIsOnlyExposedInStreamResponse() async throws {
  1083. // The implementation is copied from `StreamResponse`. The only difference is the do-catch is
  1084. // removed from the decoding initializer.
  1085. enum MyStreamResponse<Message: Decodable, Result: Decodable>: Decodable {
  1086. /// The message yielded by the callable function.
  1087. case message(Message)
  1088. /// The final result returned by the callable function.
  1089. case result(Result)
  1090. private enum CodingKeys: String, CodingKey {
  1091. case message
  1092. case result
  1093. }
  1094. public init(from decoder: any Decoder) throws {
  1095. let container = try decoder
  1096. .container(keyedBy: Self<Message, Result>.CodingKeys.self)
  1097. var allKeys = ArraySlice(container.allKeys)
  1098. guard let onlyKey = allKeys.popFirst(), allKeys.isEmpty else {
  1099. throw DecodingError
  1100. .typeMismatch(
  1101. Self<Message,
  1102. Result>.self,
  1103. DecodingError.Context(
  1104. codingPath: container.codingPath,
  1105. debugDescription: "Invalid number of keys found, expected one.",
  1106. underlyingError: nil
  1107. )
  1108. )
  1109. }
  1110. switch onlyKey {
  1111. case .message:
  1112. self = try Self
  1113. .message(container.decode(Message.self, forKey: .message))
  1114. case .result:
  1115. self = try Self
  1116. .result(container.decode(Result.self, forKey: .result))
  1117. }
  1118. }
  1119. }
  1120. let callable: Callable<[Location], MyStreamResponse<WeatherForecast, WeatherForecastReport>> =
  1121. functions
  1122. .httpsCallable("genStreamWeather")
  1123. let stream = try callable.stream([Location(name: "Toronto")])
  1124. do {
  1125. for try await _ in stream {
  1126. XCTFail("Expected error to be thrown from stream.")
  1127. }
  1128. } catch let error as FunctionsError where error.code == .dataLoss {
  1129. XCTAssertNotNil(error.errorUserInfo[NSUnderlyingErrorKey] as? DecodingError)
  1130. }
  1131. }
  1132. func testStream_ForNonStreamingCF3() async throws {
  1133. let callable: Callable<Int16, Int> = functions.httpsCallable("scalarTest")
  1134. let stream = try callable.stream(17)
  1135. do {
  1136. for try await _ in stream {
  1137. XCTFail("Expected error to be thrown from stream.")
  1138. }
  1139. } catch let error as FunctionsError where error.code == .dataLoss {
  1140. XCTAssertEqual(error.localizedDescription, "Unexpected format for streamed response.")
  1141. }
  1142. }
  1143. func testStream_EmptyStream() async throws {
  1144. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStreamEmpty")
  1145. var streamContents: [String] = []
  1146. for try await response in try callable.stream() {
  1147. streamContents.append(response)
  1148. }
  1149. XCTAssertEqual(streamContents, [])
  1150. }
  1151. func testStream_ResultOnly() async throws {
  1152. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStreamResultOnly")
  1153. let stream = try callable.stream()
  1154. for try await _ in stream {
  1155. // The stream should not yield anything, so this should not be reached.
  1156. XCTFail("Stream should not yield any messages")
  1157. }
  1158. // Because StreamResponse was not used, the result is not accessible,
  1159. // but the message should not throw.
  1160. }
  1161. func testStream_ResultOnly_StreamResponse() async throws {
  1162. struct EmptyResponse: Decodable {}
  1163. let callable: Callable<EmptyRequest, StreamResponse<EmptyResponse, String>> = functions
  1164. .httpsCallable(
  1165. "genStreamResultOnly"
  1166. )
  1167. let stream = try callable.stream()
  1168. var streamResult = ""
  1169. for try await response in stream {
  1170. switch response {
  1171. case .message:
  1172. XCTFail("Stream should not yield any messages")
  1173. case let .result(result):
  1174. streamResult = result
  1175. }
  1176. }
  1177. // The hardcoded string matches the CF3's return value.
  1178. XCTAssertEqual(streamResult, "Only a result")
  1179. }
  1180. func testStream_UnexpectedType() async throws {
  1181. // This function yields strings, not integers.
  1182. let callable: Callable<EmptyRequest, Int> = functions.httpsCallable("genStream")
  1183. let stream = try callable.stream()
  1184. do {
  1185. for try await _ in stream {
  1186. XCTFail("Expected error to be thrown from stream.")
  1187. }
  1188. } catch let error as FunctionsError where error.code == .dataLoss {
  1189. XCTAssertNotNil(error.errorUserInfo[NSUnderlyingErrorKey] as? DecodingError)
  1190. }
  1191. }
  1192. func testStream_Timeout() async throws {
  1193. var callable: Callable<EmptyRequest, String> = functions.httpsCallable("timeoutTest")
  1194. // Set a short timeout
  1195. callable.timeoutInterval = 0.01 // 10 milliseconds
  1196. let stream = try callable.stream()
  1197. do {
  1198. for try await _ in stream {
  1199. XCTFail("Expected error to be thrown from stream.")
  1200. }
  1201. } catch let error as FunctionsError where error.code == .unavailable {
  1202. // This should be a timeout error.
  1203. XCTAssertEqual(
  1204. error.localizedDescription,
  1205. "The operation couldn’t be completed. (com.firebase.functions error 14.)"
  1206. )
  1207. XCTAssertNotNil(error.errorUserInfo[NSUnderlyingErrorKey] as? URLError)
  1208. }
  1209. }
  1210. func testStream_LargeData() async throws {
  1211. func generateLargeString() -> String {
  1212. var largeString = ""
  1213. for _ in 0 ..< 10000 {
  1214. largeString += "A"
  1215. }
  1216. return largeString
  1217. }
  1218. let callable: Callable<EmptyRequest, String> = functions.httpsCallable("genStreamLargeData")
  1219. let stream = try callable.stream()
  1220. var concatenatedData = ""
  1221. for try await response in stream {
  1222. concatenatedData += response
  1223. }
  1224. // Assert that the concatenated data matches the expected large data.
  1225. XCTAssertEqual(concatenatedData, generateLargeString())
  1226. }
  1227. }
  1228. // MARK: - Helpers
  1229. private class AuthTokenProvider: AuthInterop {
  1230. func getUserID() -> String? {
  1231. return "fake user"
  1232. }
  1233. let token: String
  1234. init(token: String) {
  1235. self.token = token
  1236. }
  1237. func getToken(forcingRefresh: Bool, completion: (String?, Error?) -> Void) {
  1238. completion(token, nil)
  1239. }
  1240. }
  1241. private class MessagingTokenProvider: NSObject, MessagingInterop {
  1242. var fcmToken: String? { return "fakeFCMToken" }
  1243. }