StorageTests.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. class StorageTests: XCTestCase {
  22. override class func setUp() {
  23. let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
  24. gcmSenderID: "00000000000000000-00000000000-000000000")
  25. options.projectID = "myProjectID"
  26. FirebaseApp.configure(name: "test-StorageTests", options: options)
  27. }
  28. func testBucketNotEnforced() throws {
  29. let app = try getApp(bucket: "")
  30. let storage = Storage.storage(app: app)
  31. _ = storage.reference(forURL: "gs://benwu-test1.storage.firebase.com/child")
  32. _ = storage.reference(forURL: "gs://benwu-test2.storage.firebase.com/child")
  33. }
  34. func testBucketEnforced() throws {
  35. let app = try getApp(bucket: "bucket")
  36. let storage = Storage.storage(app: app, url: "gs://benwu-test1.storage.firebase.com")
  37. let url = try XCTUnwrap(URL(string: "gs://benwu-test2.storage.firebase.com/child"))
  38. XCTAssertThrowsError(try storage.reference(for: url), "This was supposed to fail.") { error in
  39. XCTAssertEqual(
  40. "\(error)",
  41. "bucketMismatch(\"Provided bucket: `benwu-test2.storage.firebase.com` does not match the " +
  42. "Storage bucket of the current instance: `benwu-test1.storage.firebase.com`\")"
  43. )
  44. }
  45. }
  46. func testInitWithCustomURL() throws {
  47. let app = try getApp(bucket: "bucket")
  48. let storage = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
  49. XCTAssertEqual("gs://foo-bar.appspot.com/", storage.reference().description)
  50. let storage2 = Storage.storage(app: app, url: "gs://foo-bar.appspot.com/")
  51. XCTAssertEqual("gs://foo-bar.appspot.com/", storage2.reference().description)
  52. }
  53. func SKIPtestInitWithWrongScheme() throws {
  54. let app = try getApp(bucket: "bucket")
  55. XCTAssertThrowsObjCException {
  56. _ = Storage.storage(app: app, url: "http://foo-bar.appspot.com")
  57. }
  58. }
  59. func SKIPtestInitWithNoScheme() throws {
  60. let app = try getApp(bucket: "bucket")
  61. XCTAssertThrowsObjCException {
  62. _ = Storage.storage(app: app, url: "foo-bar.appspot.com")
  63. }
  64. }
  65. func testInitWithoutURL() throws {
  66. let app = try getApp(bucket: "bucket")
  67. XCTAssertNoThrowObjCException {
  68. _ = Storage.storage(app: app)
  69. }
  70. }
  71. func SKIPtestInitWithPath() throws {
  72. let app = try getApp(bucket: "bucket")
  73. XCTAssertThrowsObjCException {
  74. _ = Storage.storage(app: app, url: "gs://foo-bar.appspot.com/child")
  75. }
  76. }
  77. func testInitWithDefaultAndCustomURL() throws {
  78. let app = try getApp(bucket: "bucket")
  79. let defaultInstance = Storage.storage(app: app)
  80. let customInstance = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
  81. XCTAssertEqual("gs://foo-bar.appspot.com/", customInstance.reference().description)
  82. XCTAssertEqual("gs://bucket/", defaultInstance.reference().description)
  83. }
  84. func testStorageDefaultApp() throws {
  85. let app = try getApp(bucket: "bucket")
  86. let storage = Storage.storage(app: app)
  87. XCTAssertEqual(storage.app.name, app.name)
  88. }
  89. func SKIPtestStorageNoBucketInConfig() throws {
  90. let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
  91. gcmSenderID: "00000000000000000-00000000000-000000000")
  92. options.projectID = "myProjectID"
  93. let name = "StorageTestsNil"
  94. let app = FirebaseApp(instanceWithName: name, options: options)
  95. XCTAssertThrowsObjCException {
  96. _ = Storage.storage(app: app)
  97. }
  98. }
  99. func testStorageEmptyBucketInConfig() throws {
  100. let app = try getApp(bucket: "")
  101. let storage = Storage.storage(app: app)
  102. let ref = storage.reference(forURL: "gs://bucket/path/to/object")
  103. XCTAssertEqual(ref.bucket, "bucket")
  104. }
  105. func testStorageWrongBucketInConfig() throws {
  106. let app = try getApp(bucket: "notMyBucket")
  107. let storage = Storage.storage(app: app)
  108. let url = try XCTUnwrap(URL(string: "gs://bucket/path/to/object"))
  109. XCTAssertThrowsError(try storage.reference(for: url), "This was supposed to fail.") { error in
  110. XCTAssertEqual(
  111. "\(error)",
  112. "bucketMismatch(\"Provided bucket: `bucket` does not match the " +
  113. "Storage bucket of the current instance: `notMyBucket`\")"
  114. )
  115. }
  116. }
  117. func testUseEmulator() throws {
  118. let app = try getApp(bucket: "bucket-for-testUseEmulator")
  119. let storage = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
  120. storage.useEmulator(withHost: "localhost", port: 8080)
  121. XCTAssertNoThrow(storage.reference())
  122. }
  123. func SKIPtestUseEmulatorValidatesHost() throws {
  124. let app = try getApp(bucket: "bucket")
  125. let storage = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
  126. XCTAssertThrowsObjCException {
  127. storage.useEmulator(withHost: "", port: 8080)
  128. }
  129. }
  130. func SKIPtestUseEmulatorValidatesPort() throws {
  131. let app = try getApp(bucket: "bucket")
  132. let storage = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
  133. XCTAssertThrowsObjCException {
  134. storage.useEmulator(withHost: "localhost", port: -1)
  135. }
  136. }
  137. func SKIPtestUseEmulatorCannotBeCalledAfterObtainingReference() throws {
  138. let app = try getApp(bucket: "bucket")
  139. let storage = Storage.storage(app: app, url: "gs://benwu-test1.storage.firebase.com")
  140. _ = storage.reference()
  141. XCTAssertThrowsObjCException {
  142. storage.useEmulator(withHost: "localhost", port: 8080)
  143. }
  144. }
  145. func testRefDefaultApp() throws {
  146. let app = try getApp(bucket: "bucket")
  147. let storage = Storage(app: app, bucket: "bucket")
  148. let convenienceRef = storage.reference(forURL: "gs://bucket/path/to/object")
  149. let path = StoragePath(with: "bucket", object: "path/to/object")
  150. let builtRef = StorageReference(storage: storage, path: path)
  151. XCTAssertEqual(convenienceRef.description, builtRef.description)
  152. XCTAssertEqual(convenienceRef.storage.app, builtRef.storage.app)
  153. }
  154. func testRefCustomApp() throws {
  155. let secondApp = try getApp(bucket: "bucket2")
  156. let storage2 = Storage.storage(app: secondApp)
  157. let convenienceRef = storage2.reference(forURL: "gs://bucket2/path/to/object")
  158. let path = StoragePath(with: "bucket2", object: "path/to/object")
  159. let builtRef = StorageReference(storage: storage2, path: path)
  160. XCTAssertEqual(convenienceRef.description, builtRef.description)
  161. XCTAssertEqual(convenienceRef.storage.app, builtRef.storage.app)
  162. }
  163. func testRootRefDefaultApp() throws {
  164. let app = try getApp(bucket: "bucket")
  165. let storage = Storage.storage(app: app)
  166. let convenienceRef = storage.reference()
  167. let path = StoragePath(with: "bucket")
  168. let builtRef = StorageReference(storage: storage, path: path)
  169. XCTAssertEqual(convenienceRef.description, builtRef.description)
  170. XCTAssertEqual(convenienceRef.storage.app, builtRef.storage.app)
  171. }
  172. func testRefWithPathDefaultApp() throws {
  173. let app = try getApp(bucket: "bucket")
  174. let storage = Storage.storage(app: app)
  175. let convenienceRef = storage.reference(forURL: "gs://bucket/path/to/object")
  176. let path = StoragePath(with: "bucket", object: "path/to/object")
  177. let builtRef = StorageReference(storage: storage, path: path)
  178. XCTAssertEqual(convenienceRef.description, builtRef.description)
  179. XCTAssertEqual(convenienceRef.storage.app, builtRef.storage.app)
  180. }
  181. func testEqual() throws {
  182. let app = try getApp(bucket: "bucket")
  183. let storage = Storage.storage(app: app)
  184. let copy = try XCTUnwrap(storage.copy() as? Storage)
  185. XCTAssertEqual(storage.app.name, copy.app.name)
  186. XCTAssertEqual(storage.hash, copy.hash)
  187. }
  188. func testNotEqual() throws {
  189. let app = try getApp(bucket: "bucket")
  190. let storage = Storage.storage(app: app)
  191. let secondApp = try getApp(bucket: "bucket2")
  192. let storage2 = Storage.storage(app: secondApp)
  193. XCTAssertNotEqual(storage, storage2)
  194. XCTAssertNotEqual(storage.hash, storage2.hash)
  195. }
  196. func testHash() throws {
  197. let app = try getApp(bucket: "bucket")
  198. let storage = Storage.storage(app: app)
  199. let copy = try XCTUnwrap(storage.copy() as? Storage)
  200. XCTAssertEqual(storage.app.name, copy.app.name)
  201. }
  202. func testTranslateRetryTime() {
  203. // The 1st retry attempt runs after 1 second.
  204. // The 2nd retry attempt is delayed by 2 seconds (3s total)
  205. // The 3rd retry attempt is delayed by 4 seconds (7s total)
  206. // The 4th retry attempt is delayed by 8 seconds (15s total)
  207. // The 5th retry attempt is delayed by 16 seconds (31s total)
  208. // The 6th retry attempt is delayed by 32 seconds (63s total)
  209. // Thus, we should exit just between the 5th and 6th retry attempt and cut off before 32s.
  210. XCTAssertEqual(1.0, Storage.computeRetryInterval(fromRetryTime: 1.0))
  211. XCTAssertEqual(2.0, Storage.computeRetryInterval(fromRetryTime: 2.0))
  212. XCTAssertEqual(4.0, Storage.computeRetryInterval(fromRetryTime: 4.0))
  213. XCTAssertEqual(8.0, Storage.computeRetryInterval(fromRetryTime: 10.0))
  214. XCTAssertEqual(16.0, Storage.computeRetryInterval(fromRetryTime: 20.0))
  215. XCTAssertEqual(16.0, Storage.computeRetryInterval(fromRetryTime: 30.0))
  216. XCTAssertEqual(32.0, Storage.computeRetryInterval(fromRetryTime: 40.0))
  217. XCTAssertEqual(32.0, Storage.computeRetryInterval(fromRetryTime: 50.0))
  218. XCTAssertEqual(32.0, Storage.computeRetryInterval(fromRetryTime: 60.0))
  219. }
  220. func testRetryTimeChange() throws {
  221. let app = try getApp(bucket: "")
  222. let storage = Storage.storage(app: app)
  223. XCTAssertEqual(storage.maxOperationRetryInterval, 64)
  224. storage.maxOperationRetryTime = 11
  225. XCTAssertEqual(storage.maxOperationRetryTime, 11)
  226. XCTAssertEqual(storage.maxOperationRetryInterval, 8)
  227. }
  228. // MARK: Private Helpers
  229. // Cache the app associated with each Storage bucket
  230. private static var appDictionary: [String: FirebaseApp] = [:]
  231. private func getApp(bucket: String) throws -> FirebaseApp {
  232. let savedApp = StorageTests.appDictionary[bucket]
  233. guard savedApp == nil else {
  234. return try XCTUnwrap(savedApp)
  235. }
  236. let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
  237. gcmSenderID: "00000000000000000-00000000000-000000000")
  238. options.projectID = "myProjectID"
  239. options.storageBucket = bucket
  240. let name = "StorageTests\(bucket)"
  241. let app = FirebaseApp(instanceWithName: name, options: options)
  242. StorageTests.appDictionary[bucket] = app
  243. return app
  244. }
  245. private func XCTAssertThrowsObjCException(_ closure: @escaping () -> Void) {
  246. XCTAssertThrowsError(try ExceptionCatcher.catchException(closure))
  247. }
  248. private func XCTAssertNoThrowObjCException(_ closure: @escaping () -> Void) {
  249. XCTAssertNoThrow(try ExceptionCatcher.catchException(closure))
  250. }
  251. }