StorageIntegration.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  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. //
  15. // Firebase Storage Integration tests
  16. //
  17. // To run these tests, you need to define the following access rights:
  18. // *
  19. // rules_version = '2';
  20. // service firebase.storage {
  21. // match /b/{bucket}/o {
  22. // match /{directChild=*} {
  23. // allow read: if request.auth != null;
  24. // }
  25. // match /ios {
  26. // match /public/{allPaths=**} {
  27. // allow write: if request.auth != null;
  28. // allow read: if true;
  29. // }
  30. // match /private/{allPaths=**} {
  31. // allow read, write: if false;
  32. // }
  33. // }
  34. // }
  35. // }
  36. //
  37. // You also need to enable email/password sign in and add a test user in your
  38. // Firebase Authentication settings. Your account credentials need to match
  39. // the credentials defined in `kTestUser` and `kTestPassword` in Credentials.swift.
  40. //
  41. // You can define these access rights in the Firebase Console of your project.
  42. //
  43. import Combine
  44. import FirebaseAuth
  45. import FirebaseCore
  46. import FirebaseStorage
  47. import FirebaseStorageSwift
  48. import FirebaseCombineSwift
  49. import XCTest
  50. class StorageIntegration: XCTestCase {
  51. var app: FirebaseApp!
  52. var auth: Auth!
  53. var storage: Storage!
  54. static var once = false
  55. static var signedIn = false
  56. override class func setUp() {
  57. FirebaseApp.configure()
  58. }
  59. override func setUp() {
  60. super.setUp()
  61. app = FirebaseApp.app()
  62. auth = Auth.auth(app: app)
  63. storage = Storage.storage(app: app!)
  64. if !StorageIntegration.signedIn {
  65. signInAndWait()
  66. }
  67. if !StorageIntegration.once {
  68. StorageIntegration.once = true
  69. let setupExpectation = expectation(description: "setUp")
  70. let largeFiles = ["ios/public/1mb"]
  71. let emptyFiles =
  72. ["ios/public/empty", "ios/public/list/a", "ios/public/list/b", "ios/public/list/prefix/c"]
  73. setupExpectation.expectedFulfillmentCount = largeFiles.count + emptyFiles.count
  74. do {
  75. var cancellables = Set<AnyCancellable>()
  76. let bundle = Bundle(for: StorageIntegration.self)
  77. let filePath = try XCTUnwrap(bundle.path(forResource: "1mb", ofType: "dat"),
  78. "Failed to get filePath")
  79. let data = try XCTUnwrap(try Data(contentsOf: URL(fileURLWithPath: filePath)),
  80. "Failed to load file")
  81. for file in largeFiles + emptyFiles {
  82. storage
  83. .reference()
  84. .child(file)
  85. .putData(data)
  86. .assertNoFailure()
  87. .sink { _ in
  88. setupExpectation.fulfill()
  89. }
  90. .store(in: &cancellables)
  91. }
  92. waitForExpectations()
  93. } catch {
  94. XCTFail("Error thrown setting up files in setUp")
  95. }
  96. }
  97. }
  98. override func tearDown() {
  99. app = nil
  100. storage = nil
  101. super.tearDown()
  102. }
  103. func testGetMetadata() {
  104. var cancellables = Set<AnyCancellable>()
  105. let expectation = self.expectation(description: "testGetMetadata")
  106. storage.reference().child("ios/public/1mb")
  107. .getMetadata()
  108. .assertNoFailure()
  109. .sink { metadata in
  110. XCTAssertNotNil(metadata)
  111. expectation.fulfill()
  112. }
  113. .store(in: &cancellables)
  114. waitForExpectations()
  115. }
  116. func testUpdateMetadata() {
  117. var cancellables = Set<AnyCancellable>()
  118. let expectation = self.expectation(description: #function)
  119. let meta = StorageMetadata()
  120. meta.contentType = "lol/custom"
  121. meta.customMetadata = ["lol": "custom metadata is neat",
  122. "ちかてつ": "🚇",
  123. "shinkansen": "新幹線"]
  124. storage.reference(withPath: "ios/public/1mb")
  125. .updateMetadata(meta)
  126. .assertNoFailure()
  127. .sink { metadata in
  128. XCTAssertEqual(meta.contentType, metadata.contentType)
  129. XCTAssertEqual(meta.customMetadata!["lol"], metadata.customMetadata!["lol"])
  130. XCTAssertEqual(meta.customMetadata!["ちかてつ"], metadata.customMetadata!["ちかてつ"])
  131. XCTAssertEqual(meta.customMetadata!["shinkansen"],
  132. metadata.customMetadata!["shinkansen"])
  133. expectation.fulfill()
  134. }
  135. .store(in: &cancellables)
  136. waitForExpectations()
  137. }
  138. func testDelete() throws {
  139. var cancellables = Set<AnyCancellable>()
  140. let expectation = self.expectation(description: #function)
  141. let ref = storage.reference(withPath: "ios/public/fileToDelete")
  142. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  143. ref.putData(data)
  144. .flatMap { _ in ref.delete() }
  145. .assertNoFailure()
  146. .sink { success in
  147. XCTAssertTrue(success)
  148. expectation.fulfill()
  149. }
  150. .store(in: &cancellables)
  151. waitForExpectations()
  152. }
  153. func testDeleteWithNilCompletion() throws {
  154. var cancellables = Set<AnyCancellable>()
  155. let expectation = self.expectation(description: #function)
  156. let ref = storage.reference(withPath: "ios/public/fileToDelete")
  157. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  158. ref.putData(data)
  159. .assertNoFailure()
  160. .sink { metadata in
  161. XCTAssertEqual(metadata.name, "fileToDelete")
  162. ref.delete(completion: nil)
  163. expectation.fulfill()
  164. }
  165. .store(in: &cancellables)
  166. waitForExpectations()
  167. }
  168. func testSimplePutData() throws {
  169. var cancellables = Set<AnyCancellable>()
  170. let expectation = self.expectation(description: #function)
  171. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  172. storage.reference(withPath: "ios/public/testBytesUpload")
  173. .putData(data)
  174. .assertNoFailure()
  175. .sink { metadata in
  176. XCTAssertEqual(metadata.name, "testBytesUpload")
  177. XCTAssertEqual(metadata.contentEncoding, "identity")
  178. expectation.fulfill()
  179. }
  180. .store(in: &cancellables)
  181. waitForExpectations()
  182. }
  183. func testSimplePutSpecialCharacter() throws {
  184. var cancellables = Set<AnyCancellable>()
  185. let expectation = self.expectation(description: #function)
  186. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  187. let path = "ios/public/-._~!$'()*,=:@&+;"
  188. storage.reference(withPath: path)
  189. .putData(data)
  190. .assertNoFailure()
  191. .sink { metadata in
  192. XCTAssertEqual(metadata.contentType, "application/octet-stream")
  193. expectation.fulfill()
  194. }
  195. .store(in: &cancellables)
  196. waitForExpectations()
  197. }
  198. func testSimplePutDataInBackgroundQueue() throws {
  199. var cancellables = Set<AnyCancellable>()
  200. let expectation = self.expectation(description: #function)
  201. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  202. storage.reference(withPath: "ios/public/testBytesUpload")
  203. .putData(data)
  204. .subscribe(on: DispatchQueue.global(qos: .background))
  205. .assertNoFailure()
  206. .sink { _ in
  207. expectation.fulfill()
  208. }
  209. .store(in: &cancellables)
  210. waitForExpectations()
  211. }
  212. func testSimplePutEmptyData() {
  213. var cancellables = Set<AnyCancellable>()
  214. let expectation = self.expectation(description: #function)
  215. storage
  216. .reference(withPath: "ios/public/testSimplePutEmptyData")
  217. .putData(Data())
  218. .assertNoFailure()
  219. .sink { _ in
  220. expectation.fulfill()
  221. }
  222. .store(in: &cancellables)
  223. waitForExpectations()
  224. }
  225. func testSimplePutDataUnauthorized() throws {
  226. var cancellables = Set<AnyCancellable>()
  227. let expectation = self.expectation(description: #function)
  228. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  229. storage
  230. .reference(withPath: "ios/private/secretfile.txt")
  231. .putData(data)
  232. .sink(receiveCompletion: { completion in
  233. switch completion {
  234. case .finished:
  235. XCTFail("Unexpected success return from putData)")
  236. case let .failure(error):
  237. XCTAssertEqual((error as NSError).code, StorageErrorCode.unauthorized.rawValue)
  238. expectation.fulfill()
  239. }
  240. }, receiveValue: { value in
  241. print("Received value \(value)")
  242. })
  243. .store(in: &cancellables)
  244. waitForExpectations()
  245. }
  246. func testAttemptToUploadDirectoryShouldFail() throws {
  247. let expectation = self.expectation(description: #function)
  248. var cancellables = Set<AnyCancellable>()
  249. // This `.numbers` file is actually a directory.
  250. let fileName = "HomeImprovement.numbers"
  251. let bundle = Bundle(for: StorageIntegration.self)
  252. let fileURL = try XCTUnwrap(bundle.url(forResource: fileName, withExtension: ""),
  253. "Failed to get filePath")
  254. storage
  255. .reference(withPath: "ios/public/" + fileName)
  256. .putFile(from: fileURL)
  257. .sink(receiveCompletion: { completion in
  258. switch completion {
  259. case .finished:
  260. XCTFail("Unexpected success return from putFile)")
  261. case let .failure(error):
  262. XCTAssertEqual((error as NSError).domain, StorageErrorDomain)
  263. expectation.fulfill()
  264. }
  265. }, receiveValue: { value in
  266. print("Received value \(value)")
  267. })
  268. .store(in: &cancellables)
  269. waitForExpectations()
  270. }
  271. func testPutFileWithSpecialCharacters() throws {
  272. var cancellables = Set<AnyCancellable>()
  273. let expectation = self.expectation(description: #function)
  274. let fileName = "hello&+@_ .txt"
  275. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  276. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  277. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  278. try data.write(to: fileURL, options: .atomicWrite)
  279. let ref = storage.reference(withPath: "ios/public/" + fileName)
  280. ref
  281. .putFile(from: fileURL)
  282. .assertNoFailure()
  283. .sink { _ in
  284. ref
  285. .getMetadata()
  286. .assertNoFailure()
  287. .sink { metadata in
  288. XCTAssertNotNil(metadata)
  289. expectation.fulfill()
  290. }
  291. .store(in: &cancellables)
  292. }
  293. .store(in: &cancellables)
  294. waitForExpectations()
  295. }
  296. func testSimplePutDataNoMetadata() throws {
  297. var cancellables = Set<AnyCancellable>()
  298. let expectation = self.expectation(description: #function)
  299. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  300. storage
  301. .reference(withPath: "ios/public/testSimplePutDataNoMetadata")
  302. .putData(data)
  303. .assertNoFailure()
  304. .sink { metadata in
  305. XCTAssertNotNil(metadata)
  306. expectation.fulfill()
  307. }
  308. .store(in: &cancellables)
  309. waitForExpectations()
  310. }
  311. func testSimplePutFileNoMetadata() throws {
  312. var cancellables = Set<AnyCancellable>()
  313. let expectation = self.expectation(description: #function)
  314. let fileName = "hello&+@_ .txt"
  315. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  316. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  317. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  318. try data.write(to: fileURL, options: .atomicWrite)
  319. storage
  320. .reference(withPath: "ios/public/" + fileName)
  321. .putFile(from: fileURL)
  322. .assertNoFailure()
  323. .sink { metadata in
  324. XCTAssertNotNil(metadata)
  325. expectation.fulfill()
  326. }
  327. .store(in: &cancellables)
  328. waitForExpectations()
  329. }
  330. func testSimpleGetData() {
  331. var cancellables = Set<AnyCancellable>()
  332. let expectation = self.expectation(description: #function)
  333. storage
  334. .reference(withPath: "ios/public/1mb")
  335. .getData(maxSize: 1024 * 1024)
  336. .assertNoFailure()
  337. .sink { _ in
  338. expectation.fulfill()
  339. }
  340. .store(in: &cancellables)
  341. waitForExpectations()
  342. }
  343. func testSimpleGetDataInBackgroundQueue() {
  344. var cancellables = Set<AnyCancellable>()
  345. let expectation = self.expectation(description: #function)
  346. storage
  347. .reference(withPath: "ios/public/1mb")
  348. .getData(maxSize: 1024 * 1024)
  349. .subscribe(on: DispatchQueue.global(qos: .background))
  350. .assertNoFailure()
  351. .sink { _ in
  352. expectation.fulfill()
  353. }
  354. .store(in: &cancellables)
  355. waitForExpectations()
  356. }
  357. func testSimpleGetDataWithCustomCallbackQueue() {
  358. var cancellables = Set<AnyCancellable>()
  359. let expectation = self.expectation(description: #function)
  360. let callbackQueueLabel = "customCallbackQueue"
  361. let callbackQueueKey = DispatchSpecificKey<String>()
  362. let callbackQueue = DispatchQueue(label: callbackQueueLabel)
  363. callbackQueue.setSpecific(key: callbackQueueKey, value: callbackQueueLabel)
  364. storage.callbackQueue = callbackQueue
  365. storage
  366. .reference(withPath: "ios/public/1mb")
  367. .getData(maxSize: 1024 * 1024)
  368. .assertNoFailure()
  369. .sink { _ in
  370. XCTAssertFalse(Thread.isMainThread)
  371. let currentQueueLabel = DispatchQueue.getSpecific(key: callbackQueueKey)
  372. XCTAssertEqual(currentQueueLabel, callbackQueueLabel)
  373. expectation.fulfill()
  374. // Reset the callbackQueue to default (main queue).
  375. self.storage.callbackQueue = DispatchQueue.main
  376. callbackQueue.setSpecific(key: callbackQueueKey, value: nil)
  377. }
  378. .store(in: &cancellables)
  379. waitForExpectations()
  380. }
  381. func testSimpleGetDataTooSmall() {
  382. var cancellables = Set<AnyCancellable>()
  383. let expectation = self.expectation(description: #function)
  384. storage
  385. .reference(withPath: "ios/public/1mb")
  386. .getData(maxSize: 1024)
  387. .sink(receiveCompletion: { completion in
  388. switch completion {
  389. case .finished:
  390. XCTFail("Unexpected success return from getData)")
  391. case let .failure(error):
  392. XCTAssertEqual((error as NSError).domain, StorageErrorDomain)
  393. expectation.fulfill()
  394. }
  395. }, receiveValue: { value in
  396. print("Received value \(value)")
  397. })
  398. .store(in: &cancellables)
  399. waitForExpectations()
  400. }
  401. func testSimpleGetDownloadURL() {
  402. var cancellables = Set<AnyCancellable>()
  403. let expectation = self.expectation(description: #function)
  404. // Download URL format is
  405. // "https://firebasestorage.googleapis.com/v0/b/{bucket}/o/{path}?alt=media&token={token}"
  406. let downloadURLPattern =
  407. "^https:\\/\\/firebasestorage.googleapis.com:443\\/v0\\/b\\/[^\\/]*\\/o\\/" +
  408. "ios%2Fpublic%2F1mb\\?alt=media&token=[a-z0-9-]*$"
  409. storage
  410. .reference(withPath: "ios/public/1mb")
  411. .downloadURL()
  412. .assertNoFailure()
  413. .sink { downloadURL in
  414. do {
  415. let testRegex = try NSRegularExpression(pattern: downloadURLPattern)
  416. let downloadURL = try XCTUnwrap(downloadURL, "Failed to unwrap downloadURL")
  417. let urlString = downloadURL.absoluteString
  418. XCTAssertEqual(testRegex.numberOfMatches(in: urlString,
  419. range: NSRange(location: 0,
  420. length: urlString.count)), 1)
  421. } catch {
  422. XCTFail("Throw in downloadURL completion block")
  423. }
  424. expectation.fulfill()
  425. }
  426. .store(in: &cancellables)
  427. waitForExpectations()
  428. }
  429. private func assertMetadata(actualMetadata: StorageMetadata,
  430. expectedContentType: String,
  431. expectedCustomMetadata: [String: String]) {
  432. XCTAssertEqual(actualMetadata.cacheControl, "cache-control")
  433. XCTAssertEqual(actualMetadata.contentDisposition, "content-disposition")
  434. XCTAssertEqual(actualMetadata.contentEncoding, "gzip")
  435. XCTAssertEqual(actualMetadata.contentLanguage, "de")
  436. XCTAssertEqual(actualMetadata.contentType, expectedContentType)
  437. XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
  438. for (key, value) in expectedCustomMetadata {
  439. XCTAssertEqual(actualMetadata.customMetadata![key], value)
  440. }
  441. }
  442. private func assertMetadataNil(actualMetadata: StorageMetadata) {
  443. XCTAssertNil(actualMetadata.cacheControl)
  444. XCTAssertNil(actualMetadata.contentDisposition)
  445. XCTAssertEqual(actualMetadata.contentEncoding, "identity")
  446. XCTAssertNil(actualMetadata.contentLanguage)
  447. XCTAssertNil(actualMetadata.contentType)
  448. XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
  449. XCTAssertNil(actualMetadata.customMetadata)
  450. }
  451. func testUpdateMetadata2() {
  452. var cancellables = Set<AnyCancellable>()
  453. let expectation = self.expectation(description: #function)
  454. let metadata = StorageMetadata()
  455. metadata.cacheControl = "cache-control"
  456. metadata.contentDisposition = "content-disposition"
  457. metadata.contentEncoding = "gzip"
  458. metadata.contentLanguage = "de"
  459. metadata.contentType = "content-type-a"
  460. metadata.customMetadata = ["a": "b"]
  461. let ref = storage.reference(withPath: "ios/public/1mb")
  462. ref
  463. .updateMetadata(metadata)
  464. .assertNoFailure()
  465. .sink { updatedMetadata in
  466. self.assertMetadata(actualMetadata: updatedMetadata,
  467. expectedContentType: "content-type-a",
  468. expectedCustomMetadata: ["a": "b"])
  469. let metadata = updatedMetadata
  470. metadata.contentType = "content-type-b"
  471. metadata.customMetadata = ["a": "b", "c": "d"]
  472. ref
  473. .updateMetadata(metadata)
  474. .assertNoFailure()
  475. .sink { updatedMetadata in
  476. self.assertMetadata(actualMetadata: updatedMetadata,
  477. expectedContentType: "content-type-b",
  478. expectedCustomMetadata: ["a": "b", "c": "d"])
  479. metadata.cacheControl = nil
  480. metadata.contentDisposition = nil
  481. metadata.contentEncoding = nil
  482. metadata.contentLanguage = nil
  483. metadata.contentType = nil
  484. metadata.customMetadata = nil
  485. ref
  486. .updateMetadata(metadata)
  487. .assertNoFailure()
  488. .sink { _ in
  489. expectation.fulfill()
  490. }
  491. .store(in: &cancellables)
  492. }
  493. .store(in: &cancellables)
  494. }
  495. .store(in: &cancellables)
  496. waitForExpectations()
  497. }
  498. func testPagedListFiles() {
  499. var cancellables = Set<AnyCancellable>()
  500. let expectation = self.expectation(description: #function)
  501. let ref = storage.reference(withPath: "ios/public/list")
  502. ref
  503. .list(maxResults: 2)
  504. .assertNoFailure()
  505. .sink { listResult in
  506. XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
  507. XCTAssertEqual(listResult.prefixes, [])
  508. guard let pageToken = listResult.pageToken else {
  509. XCTFail("pageToken should not be nil")
  510. expectation.fulfill()
  511. return
  512. }
  513. ref
  514. .list(maxResults: 2, pageToken: pageToken)
  515. .assertNoFailure()
  516. .sink { listResult in
  517. XCTAssertEqual(listResult.items, [])
  518. XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
  519. XCTAssertNil(listResult.pageToken, "pageToken should be nil")
  520. expectation.fulfill()
  521. }
  522. .store(in: &cancellables)
  523. }
  524. .store(in: &cancellables)
  525. waitForExpectations()
  526. }
  527. func testListAllFiles() {
  528. var cancellables = Set<AnyCancellable>()
  529. let expectation = self.expectation(description: #function)
  530. let ref = storage.reference(withPath: "ios/public/list")
  531. ref
  532. .listAll()
  533. .assertNoFailure()
  534. .sink { listResult in
  535. XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
  536. XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
  537. XCTAssertNil(listResult.pageToken, "pageToken should be nil")
  538. expectation.fulfill()
  539. }
  540. .store(in: &cancellables)
  541. waitForExpectations()
  542. }
  543. private func signInAndWait() {
  544. var cancellables = Set<AnyCancellable>()
  545. let expectation = self.expectation(description: #function)
  546. auth
  547. .signIn(withEmail: Credentials.kUserName,
  548. password: Credentials.kPassword)
  549. .assertNoFailure()
  550. .sink { _ in
  551. StorageIntegration.signedIn = true
  552. print("Successfully signed in")
  553. expectation.fulfill()
  554. }
  555. .store(in: &cancellables)
  556. waitForExpectations()
  557. }
  558. private func waitForExpectations() {
  559. let kFIRStorageIntegrationTestTimeout = 30.0
  560. waitForExpectations(timeout: kFIRStorageIntegrationTestTimeout,
  561. handler: { (error) -> Void in
  562. if let error = error {
  563. print(error)
  564. }
  565. })
  566. }
  567. }