StorageReferenceTests.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. // Copyright 2022 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 FirebaseCore
  16. @testable import FirebaseStorage
  17. import FirebaseAppCheckInterop
  18. import FirebaseAuthInterop
  19. import SharedTestUtilities
  20. import XCTest
  21. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  22. class StorageReferenceTests: XCTestCase {
  23. override class func setUp() {
  24. let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
  25. gcmSenderID: "00000000000000000-00000000000-000000000")
  26. options.projectID = "myProjectID-Ref"
  27. FirebaseApp.configure(name: "test-StorageReference", options: options)
  28. }
  29. var storage: Storage?
  30. override func setUp() {
  31. guard let app = try? getApp(bucket: "bucket") else {
  32. fatalError("")
  33. }
  34. storage = Storage.storage(app: app)
  35. }
  36. func testRoot() throws {
  37. let ref = storage!.reference(forURL: "gs://bucket/path/to/object")
  38. XCTAssertEqual(ref.root().description, "gs://bucket/")
  39. }
  40. func testRootWithURLAPI() throws {
  41. let url = try XCTUnwrap(URL(string: "gs://bucket/path/to/object"))
  42. let ref = try storage!.reference(for: url)
  43. XCTAssertEqual(ref.root().description, "gs://bucket/")
  44. }
  45. func testRootWithNoPath() throws {
  46. let ref = storage!.reference(forURL: "gs://bucket/")
  47. XCTAssertEqual(ref.root().description, "gs://bucket/")
  48. }
  49. func testMismatchedBucket() throws {
  50. do {
  51. let url = try XCTUnwrap(URL(string: "gs://bcket/"))
  52. _ = try storage!.reference(for: url)
  53. } catch let StorageError.bucketMismatch(string) {
  54. XCTAssertEqual(string, "Provided bucket: `bcket` does not match the Storage " +
  55. "bucket of the current instance: `bucket`")
  56. return
  57. }
  58. XCTFail()
  59. }
  60. func testMismatchedBucket2() throws {
  61. let url = try XCTUnwrap(URL(string: "gs://bcket/"))
  62. XCTAssertThrowsError(try storage!.reference(for: url), "This was supposed to fail.") { error in
  63. XCTAssertEqual(
  64. "\(error)",
  65. "bucketMismatch(message: \"Provided bucket: `bcket` does not match the Storage " +
  66. "bucket of the current instance: `bucket`\")"
  67. )
  68. }
  69. }
  70. func testBadBucketScheme() throws {
  71. do {
  72. let url = try XCTUnwrap(URL(string: "htttp://bucket/"))
  73. _ = try storage!.reference(for: url)
  74. } catch let StorageError.pathError(string) {
  75. XCTAssertEqual(
  76. string,
  77. "Internal error: URL scheme must be one of gs://, http://, or https://"
  78. )
  79. return
  80. }
  81. XCTFail()
  82. }
  83. func testBadBucketScheme2() throws {
  84. let url = try XCTUnwrap(URL(string: "htttp://bucket/"))
  85. XCTAssertThrowsError(try storage!.reference(for: url), "This was supposed to fail.") { error in
  86. XCTAssertEqual("\(error)",
  87. "pathError(message: \"Internal error: URL scheme must be one of gs://, " +
  88. "http://, or https://\")")
  89. }
  90. }
  91. func testSingleChild() throws {
  92. let ref = storage!.reference(forURL: "gs://bucket/")
  93. let childRef = ref.child("path")
  94. XCTAssertEqual(childRef.description, "gs://bucket/path")
  95. }
  96. func testMultipleChildrenSingleString() throws {
  97. let ref = storage!.reference(forURL: "gs://bucket/")
  98. let childRef = ref.child("path/to/object")
  99. XCTAssertEqual(childRef.description, "gs://bucket/path/to/object")
  100. }
  101. func testMultipleChildrenMultipleStrings() throws {
  102. let ref = storage!.reference(forURL: "gs://bucket/")
  103. let childRef = ref.child("path").child("to").child("object")
  104. XCTAssertEqual(childRef.description, "gs://bucket/path/to/object")
  105. }
  106. func testSameChildDifferentRef() throws {
  107. let ref = storage!.reference(forURL: "gs://bucket/")
  108. let firstRef = ref.child("1")
  109. let secondRef = ref.child("1")
  110. XCTAssertEqual(ref.description, "gs://bucket/")
  111. XCTAssertTrue(firstRef == secondRef)
  112. XCTAssertFalse(firstRef === secondRef)
  113. }
  114. func testDifferentChildDifferentRef() throws {
  115. let ref = storage!.reference(forURL: "gs://bucket/")
  116. let firstRef = ref.child("1")
  117. let secondRef = ref.child("2")
  118. XCTAssertEqual(ref.description, "gs://bucket/")
  119. XCTAssertFalse(firstRef == secondRef)
  120. XCTAssertFalse(firstRef === secondRef)
  121. }
  122. func testChildWithTrailingSlash() throws {
  123. let ref = storage!.reference(forURL: "gs://bucket/path/to/object/")
  124. XCTAssertEqual(ref.description, "gs://bucket/path/to/object")
  125. }
  126. func testChildWithLeadingSlash() throws {
  127. let ref = storage!.reference(forURL: "gs://bucket//path/to/object/")
  128. XCTAssertEqual(ref.description, "gs://bucket/path/to/object")
  129. }
  130. func testChildCompressSlashes() throws {
  131. let ref = storage!.reference(forURL: "gs://bucket//path/////to////object////")
  132. XCTAssertEqual(ref.description, "gs://bucket/path/to/object")
  133. }
  134. func testParent() throws {
  135. let ref = storage!.reference(forURL: "gs://bucket//path/to/object/")
  136. let parentRef = try XCTUnwrap(ref.parent())
  137. XCTAssertEqual(parentRef.description, "gs://bucket/path/to")
  138. }
  139. func testParentToRoot() throws {
  140. let ref = storage!.reference(forURL: "gs://bucket/path")
  141. let parentRef = try XCTUnwrap(ref.parent())
  142. XCTAssertEqual(parentRef.description, "gs://bucket/")
  143. }
  144. func testParentToRootTrailingSlash() throws {
  145. let ref = storage!.reference(forURL: "gs://bucket/path/")
  146. let parentRef = try XCTUnwrap(ref.parent())
  147. XCTAssertEqual(parentRef.description, "gs://bucket/")
  148. }
  149. func testParentAtRoot() throws {
  150. let ref = storage!.reference(forURL: "gs://bucket/")
  151. XCTAssertNil(ref.parent())
  152. }
  153. func testBucket() throws {
  154. let ref = storage!.reference(forURL: "gs://bucket//path/to/object/")
  155. XCTAssertEqual(ref.bucket, "bucket")
  156. }
  157. func testName() throws {
  158. let ref = storage!.reference(forURL: "gs://bucket/path/to/object/")
  159. XCTAssertEqual(ref.name, "object")
  160. }
  161. func testNameNoObject() throws {
  162. let ref = storage!.reference(forURL: "gs://bucket/")
  163. XCTAssertEqual(ref.name, "")
  164. }
  165. func testFullPath() throws {
  166. let ref = storage!.reference(forURL: "gs://bucket/path/to/object/")
  167. XCTAssertEqual(ref.fullPath, "path/to/object")
  168. }
  169. func testFullPathNoObject() throws {
  170. let ref = storage!.reference(forURL: "gs://bucket/")
  171. XCTAssertEqual(ref.fullPath, "")
  172. }
  173. func testCopy() throws {
  174. let ref = storage!.reference(forURL: "gs://bucket/")
  175. let copiedRef = ref.copy() as? StorageReference
  176. XCTAssertTrue(ref == copiedRef)
  177. XCTAssertFalse(ref === copiedRef)
  178. }
  179. func testReferenceWithNonExistentFileFailsWithCompletionResult() throws {
  180. let tempFilePath = NSTemporaryDirectory().appending("temp.data")
  181. let ref = storage!.reference(withPath: tempFilePath)
  182. let dummyFileURL = try XCTUnwrap(URL(string: "some_non_existing-folder/file.data"))
  183. let expectation = self.expectation(description: #function)
  184. ref.putFile(from: dummyFileURL) { result in
  185. expectation.fulfill()
  186. switch result {
  187. case .success:
  188. XCTFail("Unexpected success.", file: #file, line: #line)
  189. case let .failure(error):
  190. switch error {
  191. case let StorageError.unknown(message, _):
  192. let expectedDescription = "File at URL: \(dummyFileURL.absoluteString) " +
  193. "is not reachable. Ensure file URL is not a directory, symbolic link, or invalid url."
  194. XCTAssertEqual(expectedDescription, message)
  195. default:
  196. XCTFail("Failed to match expected Internal Error")
  197. }
  198. }
  199. }
  200. waitForExpectations(timeout: 0.5)
  201. }
  202. func testReferenceWithNonExistentFileFailsWithCompletionCallback() throws {
  203. let tempFilePath = NSTemporaryDirectory().appending("temp.data")
  204. let ref = storage!.reference(withPath: tempFilePath)
  205. let dummyFileURL = try XCTUnwrap(URL(string: "some_non_existing-folder/file.data"))
  206. let expectation = self.expectation(description: #function)
  207. ref.putFile(from: dummyFileURL) { metadata, error in
  208. expectation.fulfill()
  209. XCTAssertNil(metadata)
  210. let nsError = (error as? NSError)!
  211. XCTAssertEqual(nsError.code, StorageErrorCode.unknown.rawValue)
  212. let expectedDescription = "File at URL: \(dummyFileURL.absoluteString) " +
  213. "is not reachable. Ensure file URL is not a directory, symbolic link, or invalid url."
  214. XCTAssertEqual(expectedDescription, nsError.localizedDescription)
  215. XCTAssertEqual(nsError.domain, StorageErrorDomain)
  216. }
  217. waitForExpectations(timeout: 0.5)
  218. }
  219. func testReferenceWithNilFileFailsWithCompletionCallback() throws {
  220. let tempFilePath = NSTemporaryDirectory().appending("temp.data")
  221. let ref = storage!.reference(withPath: tempFilePath)
  222. let dummyFileURL = try XCTUnwrap(URL(string: "bad-url"))
  223. let expectation = self.expectation(description: #function)
  224. ref.putFile(from: dummyFileURL) { metadata, error in
  225. expectation.fulfill()
  226. XCTAssertNil(metadata)
  227. let nsError = (error as? NSError)!
  228. XCTAssertEqual(nsError.code, StorageErrorCode.unknown.rawValue)
  229. let expectedDescription = "File at URL: \(dummyFileURL.absoluteString) " +
  230. "is not reachable. Ensure file URL is not a directory, symbolic link, or invalid url."
  231. XCTAssertEqual(expectedDescription, nsError.localizedDescription)
  232. XCTAssertEqual(nsError.domain, StorageErrorDomain)
  233. }
  234. waitForExpectations(timeout: 1.0)
  235. }
  236. // MARK: Private Helpers
  237. // Cache the app associated with each Storage bucket
  238. private static var appDictionary: [String: FirebaseApp] = [:]
  239. private func getApp(bucket: String) throws -> FirebaseApp {
  240. let savedApp = StorageReferenceTests.appDictionary[bucket]
  241. guard savedApp == nil else {
  242. return try XCTUnwrap(savedApp)
  243. }
  244. let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
  245. gcmSenderID: "00000000000000000-00000000000-000000000")
  246. options.projectID = "myProjectID"
  247. options.storageBucket = bucket
  248. let name = "StorageTests\(bucket)"
  249. let app = FirebaseApp(instanceWithName: name, options: options)
  250. StorageReferenceTests.appDictionary[bucket] = app
  251. return app
  252. }
  253. }