SessionManagerTests.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  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. if let header = Request.authorizationHeader(user: "user", password: "password") {
  49. urlRequest.setValue(header.value, forHTTPHeaderField: header.key)
  50. }
  51. }
  52. return urlRequest
  53. }
  54. func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping 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 - Default HTTP Headers
  148. func testDefaultUserAgentHeader() {
  149. // Given, When
  150. let userAgent = SessionManager.defaultHTTPHeaders["User-Agent"]
  151. // Then
  152. let osNameVersion: String = {
  153. let version = ProcessInfo.processInfo.operatingSystemVersion
  154. let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
  155. let osName: String = {
  156. #if os(iOS)
  157. return "iOS"
  158. #elseif os(watchOS)
  159. return "watchOS"
  160. #elseif os(tvOS)
  161. return "tvOS"
  162. #elseif os(OSX)
  163. return "OS X"
  164. #elseif os(Linux)
  165. return "Linux"
  166. #else
  167. return "Unknown"
  168. #endif
  169. }()
  170. return "\(osName) \(versionString)"
  171. }()
  172. let alamofireVersion: String = {
  173. guard
  174. let afInfo = Bundle(for: SessionManager.self).infoDictionary,
  175. let build = afInfo["CFBundleShortVersionString"]
  176. else { return "Unknown" }
  177. return "Alamofire/\(build)"
  178. }()
  179. let expectedUserAgent = "Unknown/Unknown (Unknown; build:Unknown; \(osNameVersion)) \(alamofireVersion)"
  180. XCTAssertEqual(userAgent, expectedUserAgent)
  181. }
  182. // MARK: Tests - Start Requests Immediately
  183. func testSetStartRequestsImmediatelyToFalseAndResumeRequest() {
  184. // Given
  185. let manager = SessionManager()
  186. manager.startRequestsImmediately = false
  187. let url = URL(string: "https://httpbin.org/get")!
  188. let urlRequest = URLRequest(url: url)
  189. let expectation = self.expectation(description: "\(url)")
  190. var response: HTTPURLResponse?
  191. // When
  192. manager.request(resource: urlRequest)
  193. .response { resp in
  194. response = resp.response
  195. expectation.fulfill()
  196. }
  197. .resume()
  198. waitForExpectations(timeout: timeout, handler: nil)
  199. // Then
  200. XCTAssertNotNil(response, "response should not be nil")
  201. XCTAssertTrue(response?.statusCode == 200, "response status code should be 200")
  202. }
  203. // MARK: Tests - Deinitialization
  204. func testReleasingManagerWithPendingRequestDeinitializesSuccessfully() {
  205. // Given
  206. var manager: SessionManager? = SessionManager()
  207. manager?.startRequestsImmediately = false
  208. let url = URL(string: "https://httpbin.org/get")!
  209. let urlRequest = URLRequest(url: url)
  210. // When
  211. let request = manager?.request(resource: urlRequest)
  212. manager = nil
  213. // Then
  214. XCTAssertTrue(request?.task.state == .suspended, "request task state should be '.Suspended'")
  215. XCTAssertNil(manager, "manager should be nil")
  216. }
  217. func testReleasingManagerWithPendingCanceledRequestDeinitializesSuccessfully() {
  218. // Given
  219. var manager: SessionManager? = SessionManager()
  220. manager!.startRequestsImmediately = false
  221. let url = URL(string: "https://httpbin.org/get")!
  222. let urlRequest = URLRequest(url: url)
  223. // When
  224. let request = manager!.request(resource: urlRequest)
  225. request.cancel()
  226. manager = nil
  227. // Then
  228. let state = request.task.state
  229. XCTAssertTrue(state == .canceling || state == .completed, "state should be .Canceling or .Completed")
  230. XCTAssertNil(manager, "manager should be nil")
  231. }
  232. // MARK: Tests - Request Adapter
  233. func testThatSessionManagerCallsRequestAdapterWhenCreatingDataRequest() {
  234. // Given
  235. let adapter = HTTPMethodAdapter(method: .post)
  236. let sessionManager = SessionManager()
  237. sessionManager.adapter = adapter
  238. sessionManager.startRequestsImmediately = false
  239. // When
  240. let request = sessionManager.request("https://httpbin.org/get")
  241. // Then
  242. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  243. }
  244. func testThatSessionManagerCallsRequestAdapterWhenCreatingDownloadRequest() {
  245. // Given
  246. let adapter = HTTPMethodAdapter(method: .post)
  247. let sessionManager = SessionManager()
  248. sessionManager.adapter = adapter
  249. sessionManager.startRequestsImmediately = false
  250. // When
  251. let destination = DownloadRequest.suggestedDownloadDestination()
  252. let request = sessionManager.download("https://httpbin.org/get", to: destination)
  253. // Then
  254. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  255. }
  256. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithData() {
  257. // Given
  258. let adapter = HTTPMethodAdapter(method: .get)
  259. let sessionManager = SessionManager()
  260. sessionManager.adapter = adapter
  261. sessionManager.startRequestsImmediately = false
  262. // When
  263. let request = sessionManager.upload("data".data(using: .utf8)!, to: "https://httpbin.org/post")
  264. // Then
  265. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  266. }
  267. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithFile() {
  268. // Given
  269. let adapter = HTTPMethodAdapter(method: .get)
  270. let sessionManager = SessionManager()
  271. sessionManager.adapter = adapter
  272. sessionManager.startRequestsImmediately = false
  273. // When
  274. let fileURL = URL(fileURLWithPath: "/path/to/some/file.txt")
  275. let request = sessionManager.upload(fileURL, to: "https://httpbin.org/post")
  276. // Then
  277. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  278. }
  279. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithInputStream() {
  280. // Given
  281. let adapter = HTTPMethodAdapter(method: .get)
  282. let sessionManager = SessionManager()
  283. sessionManager.adapter = adapter
  284. sessionManager.startRequestsImmediately = false
  285. // When
  286. let inputStream = InputStream(data: "data".data(using: .utf8)!)
  287. let request = sessionManager.upload(inputStream, to: "https://httpbin.org/post")
  288. // Then
  289. XCTAssertEqual(request.task.originalRequest?.httpMethod, adapter.method.rawValue)
  290. }
  291. // MARK: Tests - Request Retrier
  292. func testThatSessionManagerCallsRequestRetrierWhenRequestEncountersError() {
  293. // Given
  294. let handler = RequestHandler()
  295. let sessionManager = SessionManager()
  296. sessionManager.adapter = handler
  297. sessionManager.retrier = handler
  298. let expectation = self.expectation(description: "request should eventually fail")
  299. var response: DataResponse<Any>?
  300. // When
  301. sessionManager.request("https://httpbin.org/basic-auth/user/password")
  302. .validate()
  303. .responseJSON { jsonResponse in
  304. response = jsonResponse
  305. expectation.fulfill()
  306. }
  307. waitForExpectations(timeout: timeout, handler: nil)
  308. // Then
  309. XCTAssertEqual(handler.adaptedCount, 2)
  310. XCTAssertEqual(handler.retryCount, 2)
  311. XCTAssertEqual(response?.result.isSuccess, false)
  312. }
  313. func testThatSessionManagerCallsAdapterWhenRequestIsRetried() {
  314. // Given
  315. let handler = RequestHandler()
  316. handler.shouldApplyAuthorizationHeader = true
  317. let sessionManager = SessionManager()
  318. sessionManager.adapter = handler
  319. sessionManager.retrier = handler
  320. let expectation = self.expectation(description: "request should eventually fail")
  321. var response: DataResponse<Any>?
  322. // When
  323. sessionManager.request("https://httpbin.org/basic-auth/user/password")
  324. .validate()
  325. .responseJSON { jsonResponse in
  326. response = jsonResponse
  327. expectation.fulfill()
  328. }
  329. waitForExpectations(timeout: timeout, handler: nil)
  330. // Then
  331. XCTAssertEqual(handler.adaptedCount, 2)
  332. XCTAssertEqual(handler.retryCount, 1)
  333. XCTAssertEqual(response?.result.isSuccess, true)
  334. }
  335. }
  336. // MARK: -
  337. class SessionManagerConfigurationHeadersTestCase: BaseTestCase {
  338. enum ConfigurationType {
  339. case `default`, ephemeral, background
  340. }
  341. func testThatDefaultConfigurationHeadersAreSentWithRequest() {
  342. // Given, When, Then
  343. executeAuthorizationHeaderTest(for: .default)
  344. }
  345. func testThatEphemeralConfigurationHeadersAreSentWithRequest() {
  346. // Given, When, Then
  347. executeAuthorizationHeaderTest(for: .ephemeral)
  348. }
  349. // ⚠️ This test has been removed as a result of rdar://26870455 in Xcode 8 Seed 1
  350. // func testThatBackgroundConfigurationHeadersAreSentWithRequest() {
  351. // // Given, When, Then
  352. // executeAuthorizationHeaderTest(for: .background)
  353. // }
  354. private func executeAuthorizationHeaderTest(for type: ConfigurationType) {
  355. // Given
  356. let manager: SessionManager = {
  357. let configuration: URLSessionConfiguration = {
  358. let configuration: URLSessionConfiguration
  359. switch type {
  360. case .default:
  361. configuration = .default
  362. case .ephemeral:
  363. configuration = .ephemeral
  364. case .background:
  365. let identifier = "org.alamofire.test.manager-configuration-tests"
  366. configuration = .background(withIdentifier: identifier)
  367. }
  368. var headers = SessionManager.defaultHTTPHeaders
  369. headers["Authorization"] = "Bearer 123456"
  370. configuration.httpAdditionalHeaders = headers
  371. return configuration
  372. }()
  373. return SessionManager(configuration: configuration)
  374. }()
  375. let expectation = self.expectation(description: "request should complete successfully")
  376. var response: DataResponse<Any>?
  377. // When
  378. manager.request("https://httpbin.org/headers")
  379. .responseJSON { closureResponse in
  380. response = closureResponse
  381. expectation.fulfill()
  382. }
  383. waitForExpectations(timeout: timeout, handler: nil)
  384. // Then
  385. if let response = response {
  386. XCTAssertNotNil(response.request, "request should not be nil")
  387. XCTAssertNotNil(response.response, "response should not be nil")
  388. XCTAssertNotNil(response.data, "data should not be nil")
  389. XCTAssertTrue(response.result.isSuccess, "result should be a success")
  390. // The `as NSString` cast is necessary due to a compiler bug. See the following rdar for more info.
  391. // - https://openradar.appspot.com/radar?id=5517037090635776
  392. if
  393. let headers = (response.result.value as AnyObject?)?["headers" as NSString] as? [String: String],
  394. let authorization = headers["Authorization"]
  395. {
  396. XCTAssertEqual(authorization, "Bearer 123456", "authorization header value does not match")
  397. } else {
  398. XCTFail("failed to extract authorization header value")
  399. }
  400. } else {
  401. XCTFail("response should not be nil")
  402. }
  403. }
  404. }