SessionManagerTests.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. //
  2. // SessionManagerTests.swift
  3. //
  4. // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. @testable import Alamofire
  25. import Foundation
  26. import XCTest
  27. class SessionManagerTestCase: BaseTestCase {
  28. // MARK: Helper Types
  29. private class HTTPMethodAdapter: RequestAdapter {
  30. let method: HTTPMethod
  31. init(method: HTTPMethod) {
  32. self.method = method
  33. }
  34. func adapt(_ urlRequest: URLRequest) -> URLRequest {
  35. var urlRequest = urlRequest
  36. urlRequest.httpMethod = method.rawValue
  37. return urlRequest
  38. }
  39. }
  40. private class RequestHandler: RequestAdapter, RequestRetrier {
  41. var adaptedCount = 0
  42. var retryCount = 0
  43. var shouldApplyAuthorizationHeader = false
  44. func adapt(_ urlRequest: URLRequest) -> URLRequest {
  45. var urlRequest = urlRequest
  46. adaptedCount += 1
  47. if shouldApplyAuthorizationHeader && adaptedCount > 1 {
  48. Request.authorizationHeaderFrom(user: "user", password: "password").forEach { header, value in
  49. urlRequest.setValue(value, forHTTPHeaderField: header)
  50. }
  51. }
  52. return urlRequest
  53. }
  54. func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: RequestRetryCompletion) {
  55. retryCount += 1
  56. if retryCount < 2 {
  57. completion(true, 0.0)
  58. } else {
  59. completion(false, 0.0)
  60. }
  61. }
  62. }
  63. // MARK: Tests - Initialization
  64. func testInitializerWithDefaultArguments() {
  65. // Given, When
  66. let manager = SessionManager()
  67. // Then
  68. XCTAssertNotNil(manager.session.delegate, "session delegate should not be nil")
  69. XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate")
  70. XCTAssertNil(manager.session.serverTrustPolicyManager, "session server trust policy manager should be nil")
  71. }
  72. func testInitializerWithSpecifiedArguments() {
  73. // Given
  74. let configuration = URLSessionConfiguration.default
  75. let delegate = SessionDelegate()
  76. let serverTrustPolicyManager = ServerTrustPolicyManager(policies: [:])
  77. // When
  78. let manager = SessionManager(
  79. configuration: configuration,
  80. delegate: delegate,
  81. serverTrustPolicyManager: serverTrustPolicyManager
  82. )
  83. // Then
  84. XCTAssertNotNil(manager.session.delegate, "session delegate should not be nil")
  85. XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate")
  86. XCTAssertNotNil(manager.session.serverTrustPolicyManager, "session server trust policy manager should not be nil")
  87. }
  88. func testThatFailableInitializerSucceedsWithDefaultArguments() {
  89. // Given
  90. let delegate = SessionDelegate()
  91. let session: URLSession = {
  92. let configuration = URLSessionConfiguration.default
  93. return URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
  94. }()
  95. // When
  96. let manager = SessionManager(session: session, delegate: delegate)
  97. // Then
  98. if let manager = manager {
  99. XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate")
  100. XCTAssertNil(manager.session.serverTrustPolicyManager, "session server trust policy manager should be nil")
  101. } else {
  102. XCTFail("manager should not be nil")
  103. }
  104. }
  105. func testThatFailableInitializerSucceedsWithSpecifiedArguments() {
  106. // Given
  107. let delegate = SessionDelegate()
  108. let session: URLSession = {
  109. let configuration = URLSessionConfiguration.default
  110. return URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
  111. }()
  112. let serverTrustPolicyManager = ServerTrustPolicyManager(policies: [:])
  113. // When
  114. let manager = SessionManager(session: session, delegate: delegate, serverTrustPolicyManager: serverTrustPolicyManager)
  115. // Then
  116. if let manager = manager {
  117. XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate")
  118. XCTAssertNotNil(manager.session.serverTrustPolicyManager, "session server trust policy manager should not be nil")
  119. } else {
  120. XCTFail("manager should not be nil")
  121. }
  122. }
  123. func testThatFailableInitializerFailsWithWhenDelegateDoesNotEqualSessionDelegate() {
  124. // Given
  125. let delegate = SessionDelegate()
  126. let session: URLSession = {
  127. let configuration = URLSessionConfiguration.default
  128. return URLSession(configuration: configuration, delegate: SessionDelegate(), delegateQueue: nil)
  129. }()
  130. // When
  131. let manager = SessionManager(session: session, delegate: delegate)
  132. // Then
  133. XCTAssertNil(manager, "manager should be nil")
  134. }
  135. func testThatFailableInitializerFailsWhenSessionDelegateIsNil() {
  136. // Given
  137. let delegate = SessionDelegate()
  138. let session: URLSession = {
  139. let configuration = URLSessionConfiguration.default
  140. return URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
  141. }()
  142. // When
  143. let manager = SessionManager(session: session, delegate: delegate)
  144. // Then
  145. XCTAssertNil(manager, "manager should be nil")
  146. }
  147. // MARK: Tests - Start Requests Immediately
  148. func testSetStartRequestsImmediatelyToFalseAndResumeRequest() {
  149. // Given
  150. let manager = SessionManager()
  151. manager.startRequestsImmediately = false
  152. let url = URL(string: "https://httpbin.org/get")!
  153. let urlRequest = URLRequest(url: url)
  154. let expectation = self.expectation(description: "\(url)")
  155. var response: HTTPURLResponse?
  156. // When
  157. manager.request(urlRequest)
  158. .response { _, responseResponse, _, _ in
  159. response = responseResponse
  160. expectation.fulfill()
  161. }
  162. .resume()
  163. waitForExpectations(timeout: timeout, handler: nil)
  164. // Then
  165. XCTAssertNotNil(response, "response should not be nil")
  166. XCTAssertTrue(response?.statusCode == 200, "response status code should be 200")
  167. }
  168. // MARK: Tests - Deinitialization
  169. func testReleasingManagerWithPendingRequestDeinitializesSuccessfully() {
  170. // Given
  171. var manager: SessionManager? = SessionManager()
  172. manager?.startRequestsImmediately = false
  173. let url = URL(string: "https://httpbin.org/get")!
  174. let urlRequest = URLRequest(url: url)
  175. // When
  176. let request = manager?.request(urlRequest)
  177. manager = nil
  178. // Then
  179. XCTAssertTrue(request?.task.state == .suspended, "request task state should be '.Suspended'")
  180. XCTAssertNil(manager, "manager should be nil")
  181. }
  182. func testReleasingManagerWithPendingCanceledRequestDeinitializesSuccessfully() {
  183. // Given
  184. var manager: SessionManager? = SessionManager()
  185. manager!.startRequestsImmediately = false
  186. let url = URL(string: "https://httpbin.org/get")!
  187. let urlRequest = URLRequest(url: url)
  188. // When
  189. let request = manager!.request(urlRequest)
  190. request.cancel()
  191. manager = nil
  192. // Then
  193. let state = request.task.state
  194. XCTAssertTrue(state == .canceling || state == .completed, "state should be .Canceling or .Completed")
  195. XCTAssertNil(manager, "manager should be nil")
  196. }
  197. // MARK: Tests - Request Adapter
  198. func testThatSessionManagerCallsRequestAdapterWhenCreatingDataRequest() {
  199. // Given
  200. let adapter = HTTPMethodAdapter(method: .post)
  201. let sessionManager = SessionManager()
  202. sessionManager.adapter = adapter
  203. sessionManager.startRequestsImmediately = false
  204. // When
  205. let request = sessionManager.request("https://httpbin.org/get", withMethod: .get)
  206. // Then
  207. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  208. }
  209. func testThatSessionManagerCallsRequestAdapterWhenCreatingDownloadRequest() {
  210. // Given
  211. let adapter = HTTPMethodAdapter(method: .post)
  212. let sessionManager = SessionManager()
  213. sessionManager.adapter = adapter
  214. sessionManager.startRequestsImmediately = false
  215. // When
  216. let destination = Request.suggestedDownloadDestination()
  217. let request = sessionManager.download("https://httpbin.org/get", to: destination, withMethod: .get)
  218. // Then
  219. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  220. }
  221. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithData() {
  222. // Given
  223. let adapter = HTTPMethodAdapter(method: .get)
  224. let sessionManager = SessionManager()
  225. sessionManager.adapter = adapter
  226. sessionManager.startRequestsImmediately = false
  227. // When
  228. let request = sessionManager.upload("data".data(using: .utf8)!, to: "https://httpbin.org/post", withMethod: .post)
  229. // Then
  230. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  231. }
  232. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithFile() {
  233. // Given
  234. let adapter = HTTPMethodAdapter(method: .get)
  235. let sessionManager = SessionManager()
  236. sessionManager.adapter = adapter
  237. sessionManager.startRequestsImmediately = false
  238. // When
  239. let fileURL = URL(fileURLWithPath: "/path/to/some/file.txt")
  240. let request = sessionManager.upload(fileURL, to: "https://httpbin.org/post", withMethod: .post)
  241. // Then
  242. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  243. }
  244. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithInputStream() {
  245. // Given
  246. let adapter = HTTPMethodAdapter(method: .get)
  247. let sessionManager = SessionManager()
  248. sessionManager.adapter = adapter
  249. sessionManager.startRequestsImmediately = false
  250. // When
  251. let inputStream = InputStream(data: "data".data(using: .utf8)!)
  252. let request = sessionManager.upload(inputStream, to: "https://httpbin.org/post", withMethod: .post)
  253. // Then
  254. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  255. }
  256. // MARK: Tests - Request Retrier
  257. func testThatSessionManagerCallsRequestRetrierWhenRequestEncountersError() {
  258. // Given
  259. let handler = RequestHandler()
  260. let sessionManager = SessionManager()
  261. sessionManager.adapter = handler
  262. sessionManager.retrier = handler
  263. let expectation = self.expectation(description: "request should eventually fail")
  264. var response: Response<Any>?
  265. // When
  266. sessionManager.request("https://httpbin.org/basic-auth/user/password", withMethod: .get)
  267. .validate()
  268. .responseJSON { jsonResponse in
  269. response = jsonResponse
  270. expectation.fulfill()
  271. }
  272. waitForExpectations(timeout: timeout, handler: nil)
  273. // Then
  274. XCTAssertEqual(handler.adaptedCount, 2)
  275. XCTAssertEqual(handler.retryCount, 2)
  276. XCTAssertEqual(response?.result.isSuccess, false)
  277. }
  278. func testThatSessionManagerCallsAdapterWhenRequestIsRetried() {
  279. // Given
  280. let handler = RequestHandler()
  281. handler.shouldApplyAuthorizationHeader = true
  282. let sessionManager = SessionManager()
  283. sessionManager.adapter = handler
  284. sessionManager.retrier = handler
  285. let expectation = self.expectation(description: "request should eventually fail")
  286. var response: Response<Any>?
  287. // When
  288. sessionManager.request("https://httpbin.org/basic-auth/user/password", withMethod: .get)
  289. .validate()
  290. .responseJSON { jsonResponse in
  291. response = jsonResponse
  292. expectation.fulfill()
  293. }
  294. waitForExpectations(timeout: timeout, handler: nil)
  295. // Then
  296. XCTAssertEqual(handler.adaptedCount, 2)
  297. XCTAssertEqual(handler.retryCount, 1)
  298. XCTAssertEqual(response?.result.isSuccess, true)
  299. }
  300. }
  301. // MARK: -
  302. class SessionManagerConfigurationHeadersTestCase: BaseTestCase {
  303. enum ConfigurationType {
  304. case `default`, ephemeral, background
  305. }
  306. func testThatDefaultConfigurationHeadersAreSentWithRequest() {
  307. // Given, When, Then
  308. executeAuthorizationHeaderTest(for: .default)
  309. }
  310. func testThatEphemeralConfigurationHeadersAreSentWithRequest() {
  311. // Given, When, Then
  312. executeAuthorizationHeaderTest(for: .ephemeral)
  313. }
  314. // ⚠️ This test has been removed as a result of rdar://26870455 in Xcode 8 Seed 1
  315. // func testThatBackgroundConfigurationHeadersAreSentWithRequest() {
  316. // // Given, When, Then
  317. // executeAuthorizationHeaderTest(for: .background)
  318. // }
  319. private func executeAuthorizationHeaderTest(for type: ConfigurationType) {
  320. // Given
  321. let manager: SessionManager = {
  322. let configuration: URLSessionConfiguration = {
  323. let configuration: URLSessionConfiguration
  324. switch type {
  325. case .default:
  326. configuration = .default
  327. case .ephemeral:
  328. configuration = .ephemeral
  329. case .background:
  330. let identifier = "org.alamofire.test.manager-configuration-tests"
  331. configuration = .background(withIdentifier: identifier)
  332. }
  333. var headers = SessionManager.defaultHTTPHeaders
  334. headers["Authorization"] = "Bearer 123456"
  335. configuration.httpAdditionalHeaders = headers
  336. return configuration
  337. }()
  338. return SessionManager(configuration: configuration)
  339. }()
  340. let expectation = self.expectation(description: "request should complete successfully")
  341. var response: Response<Any>?
  342. // When
  343. manager.request("https://httpbin.org/headers", withMethod: .get)
  344. .responseJSON { closureResponse in
  345. response = closureResponse
  346. expectation.fulfill()
  347. }
  348. waitForExpectations(timeout: timeout, handler: nil)
  349. // Then
  350. if let response = response {
  351. XCTAssertNotNil(response.request, "request should not be nil")
  352. XCTAssertNotNil(response.response, "response should not be nil")
  353. XCTAssertNotNil(response.data, "data should not be nil")
  354. XCTAssertTrue(response.result.isSuccess, "result should be a success")
  355. // The `as NSString` cast is necessary due to a compiler bug. See the following rdar for more info.
  356. // - https://openradar.appspot.com/radar?id=5517037090635776
  357. if
  358. let headers = (response.result.value as AnyObject?)?["headers" as NSString] as? [String: String],
  359. let authorization = headers["Authorization"]
  360. {
  361. XCTAssertEqual(authorization, "Bearer 123456", "authorization header value does not match")
  362. } else {
  363. XCTFail("failed to extract authorization header value")
  364. }
  365. } else {
  366. XCTFail("response should not be nil")
  367. }
  368. }
  369. }