SessionManagerTests.swift 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. //
  2. // SessionManagerTests.swift
  3. //
  4. // Copyright (c) 2014-2018 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. let throwsError: Bool
  32. init(method: HTTPMethod, throwsError: Bool = false) {
  33. self.method = method
  34. self.throwsError = throwsError
  35. }
  36. func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
  37. guard !throwsError else { throw AFError.invalidURL(url: "") }
  38. var urlRequest = urlRequest
  39. urlRequest.httpMethod = method.rawValue
  40. return urlRequest
  41. }
  42. }
  43. private class RequestHandler: RequestAdapter, RequestRetrier {
  44. var adaptedCount = 0
  45. var retryCount = 0
  46. var retryErrors: [Error] = []
  47. var shouldApplyAuthorizationHeader = false
  48. var throwsErrorOnSecondAdapt = false
  49. func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
  50. if throwsErrorOnSecondAdapt && adaptedCount == 1 {
  51. throwsErrorOnSecondAdapt = false
  52. throw AFError.invalidURL(url: "")
  53. }
  54. var urlRequest = urlRequest
  55. adaptedCount += 1
  56. if shouldApplyAuthorizationHeader && adaptedCount > 1 {
  57. if let header = HTTPHeaders.authorization(withUsername: "user", password: "password").first {
  58. urlRequest.setValue(header.value, forHTTPHeaderField: header.key)
  59. }
  60. }
  61. return urlRequest
  62. }
  63. func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
  64. retryCount += 1
  65. retryErrors.append(error)
  66. if retryCount < 2 {
  67. completion(true, 0.0)
  68. } else {
  69. completion(false, 0.0)
  70. }
  71. }
  72. }
  73. private class UploadHandler: RequestAdapter, RequestRetrier {
  74. var adaptedCount = 0
  75. var retryCount = 0
  76. var retryErrors: [Error] = []
  77. func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
  78. adaptedCount += 1
  79. if adaptedCount == 1 { throw AFError.invalidURL(url: "") }
  80. return urlRequest
  81. }
  82. func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
  83. retryCount += 1
  84. retryErrors.append(error)
  85. completion(true, 0.0)
  86. }
  87. }
  88. // MARK: Tests - Initialization
  89. func testInitializerWithDefaultArguments() {
  90. // Given, When
  91. let manager = SessionManager()
  92. // Then
  93. XCTAssertNotNil(manager.session.delegate, "session delegate should not be nil")
  94. XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate")
  95. XCTAssertNil(manager.serverTrustManager, "session server trust policy manager should be nil")
  96. }
  97. func testInitializerWithSpecifiedArguments() {
  98. // Given
  99. let configuration = URLSessionConfiguration.default
  100. let delegate = SessionDelegate()
  101. let serverTrustManager = ServerTrustManager(evaluators: [:])
  102. // When
  103. let manager = SessionManager(configuration: configuration,
  104. delegate: delegate,
  105. serverTrustManager: serverTrustManager)
  106. // Then
  107. XCTAssertNotNil(manager.session.delegate, "session delegate should not be nil")
  108. XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate")
  109. XCTAssertNotNil(manager.serverTrustManager, "session server trust policy manager should not be nil")
  110. }
  111. // TODO: If we allow intialization with a URLSession, we can't control the queues, so do we want this API?
  112. // func testThatFailableInitializerSucceedsWithDefaultArguments() {
  113. // // Given
  114. // let delegate = SessionDelegate()
  115. // let session: URLSession = {
  116. // let configuration = URLSessionConfiguration.default
  117. // return URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
  118. // }()
  119. //
  120. // // When
  121. // let manager = SessionManager(session: session, delegate: delegate)
  122. //
  123. // // Then
  124. // if let manager = manager {
  125. // XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate")
  126. // XCTAssertNil(manager.session.serverTrustManager, "session server trust policy manager should be nil")
  127. // } else {
  128. // XCTFail("manager should not be nil")
  129. // }
  130. // }
  131. //
  132. // func testThatFailableInitializerSucceedsWithSpecifiedArguments() {
  133. // // Given
  134. // let delegate = SessionDelegate()
  135. // let session: URLSession = {
  136. // let configuration = URLSessionConfiguration.default
  137. // return URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
  138. // }()
  139. //
  140. // let serverTrustManager = ServerTrustManager(evaluators: [:])
  141. //
  142. // // When
  143. // let manager = SessionManager(session: session, delegate: delegate, serverTrustManager: serverTrustManager)
  144. //
  145. // // Then
  146. // if let manager = manager {
  147. // XCTAssertTrue(manager.delegate === manager.session.delegate, "manager delegate should equal session delegate")
  148. // XCTAssertNotNil(manager.session.serverTrustManager, "session server trust policy manager should not be nil")
  149. // } else {
  150. // XCTFail("manager should not be nil")
  151. // }
  152. // }
  153. //
  154. // func testThatFailableInitializerFailsWithWhenDelegateDoesNotEqualSessionDelegate() {
  155. // // Given
  156. // let delegate = SessionDelegate()
  157. // let session: URLSession = {
  158. // let configuration = URLSessionConfiguration.default
  159. // return URLSession(configuration: configuration, delegate: SessionDelegate(), delegateQueue: nil)
  160. // }()
  161. //
  162. // // When
  163. // let manager = SessionManager(session: session, delegate: delegate)
  164. //
  165. // // Then
  166. // XCTAssertNil(manager, "manager should be nil")
  167. // }
  168. //
  169. // func testThatFailableInitializerFailsWhenSessionDelegateIsNil() {
  170. // // Given
  171. // let delegate = SessionDelegate()
  172. // let session: URLSession = {
  173. // let configuration = URLSessionConfiguration.default
  174. // return URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
  175. // }()
  176. //
  177. // // When
  178. // let manager = SessionManager(session: session, delegate: delegate)
  179. //
  180. // // Then
  181. // XCTAssertNil(manager, "manager should be nil")
  182. // }
  183. // MARK: Tests - Default HTTP Headers
  184. func testDefaultUserAgentHeader() {
  185. // Given, When
  186. let userAgent = SessionManager.defaultHTTPHeaders["User-Agent"]
  187. // Then
  188. let osNameVersion: String = {
  189. let version = ProcessInfo.processInfo.operatingSystemVersion
  190. let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
  191. let osName: String = {
  192. #if os(iOS)
  193. return "iOS"
  194. #elseif os(watchOS)
  195. return "watchOS"
  196. #elseif os(tvOS)
  197. return "tvOS"
  198. #elseif os(macOS)
  199. return "macOS"
  200. #elseif os(Linux)
  201. return "Linux"
  202. #else
  203. return "Unknown"
  204. #endif
  205. }()
  206. return "\(osName) \(versionString)"
  207. }()
  208. let alamofireVersion: String = {
  209. guard
  210. let afInfo = Bundle(for: SessionManager.self).infoDictionary,
  211. let build = afInfo["CFBundleShortVersionString"]
  212. else { return "Unknown" }
  213. return "Alamofire/\(build)"
  214. }()
  215. let expectedUserAgent = "Unknown/Unknown (Unknown; build:Unknown; \(osNameVersion)) \(alamofireVersion)"
  216. XCTAssertEqual(userAgent, expectedUserAgent)
  217. }
  218. // MARK: Tests - Supported Accept-Encodings
  219. func testDefaultAcceptEncodingSupportsAppropriateEncodingsOnAppropriateSystems() {
  220. // Given
  221. let brotliURL = URL(string: "https://httpbin.org/brotli")!
  222. let gzipURL = URL(string: "https://httpbin.org/gzip")!
  223. let deflateURL = URL(string: "https://httpbin.org/deflate")!
  224. let brotliExpectation = expectation(description: "brotli request should complete")
  225. let gzipExpectation = expectation(description: "gzip request should complete")
  226. let deflateExpectation = expectation(description: "deflate request should complete")
  227. var brotliResponse: DataResponse<Any>?
  228. var gzipResponse: DataResponse<Any>?
  229. var deflateResponse: DataResponse<Any>?
  230. // When
  231. Alamofire.request(brotliURL).responseJSON { (response) in
  232. brotliResponse = response
  233. brotliExpectation.fulfill()
  234. }
  235. Alamofire.request(gzipURL).responseJSON { (response) in
  236. gzipResponse = response
  237. gzipExpectation.fulfill()
  238. }
  239. Alamofire.request(deflateURL).responseJSON { (response) in
  240. deflateResponse = response
  241. deflateExpectation.fulfill()
  242. }
  243. waitForExpectations(timeout: timeout, handler: nil)
  244. // Then
  245. if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) {
  246. XCTAssertTrue(brotliResponse?.result.isSuccess == true)
  247. } else {
  248. XCTAssertTrue(brotliResponse?.result.isFailure == true)
  249. }
  250. XCTAssertTrue(gzipResponse?.result.isSuccess == true)
  251. XCTAssertTrue(deflateResponse?.result.isSuccess == true)
  252. }
  253. // MARK: Tests - Start Requests Immediately
  254. func testSetStartRequestsImmediatelyToFalseAndResumeRequest() {
  255. // Given
  256. let delegate = SessionDelegate(startRequestsImmediately: false)
  257. let manager = SessionManager(delegate: delegate)
  258. let url = URL(string: "https://httpbin.org/get")!
  259. let urlRequest = URLRequest(url: url)
  260. let expectation = self.expectation(description: "\(url)")
  261. var response: HTTPURLResponse?
  262. // When
  263. manager.request(urlRequest)
  264. .response { resp in
  265. response = resp.response
  266. expectation.fulfill()
  267. }
  268. .resume()
  269. waitForExpectations(timeout: timeout, handler: nil)
  270. // Then
  271. XCTAssertNotNil(response, "response should not be nil")
  272. XCTAssertTrue(response?.statusCode == 200, "response status code should be 200")
  273. }
  274. func testSetStartRequestsImmediatelyToFalseAndCancelledCallsResponseHandlers() {
  275. // Given
  276. let delegate = SessionDelegate(startRequestsImmediately: false)
  277. let manager = SessionManager(delegate: delegate)
  278. let url = URL(string: "https://httpbin.org/get")!
  279. let urlRequest = URLRequest(url: url)
  280. let expectation = self.expectation(description: "\(url)")
  281. var response: DataResponse<Data?>?
  282. // When
  283. let request = manager.request(urlRequest)
  284. .cancel()
  285. .response { resp in
  286. response = resp
  287. expectation.fulfill()
  288. }
  289. waitForExpectations(timeout: timeout, handler: nil)
  290. // Then
  291. XCTAssertNotNil(response, "response should not be nil")
  292. XCTAssertTrue(request.isCancelled)
  293. XCTAssertNil(request.task)
  294. guard let error = request.error as? AFError, case .explicitlyCancelled = error else { XCTFail(); return }
  295. }
  296. func testSetStartRequestsImmediatelyToFalseAndResumeThenCancelRequestHasCorrectOutput() {
  297. // Given
  298. let delegate = SessionDelegate(startRequestsImmediately: false)
  299. let manager = SessionManager(delegate: delegate)
  300. let url = URL(string: "https://httpbin.org/get")!
  301. let urlRequest = URLRequest(url: url)
  302. let expectation = self.expectation(description: "\(url)")
  303. var response: DataResponse<Data?>?
  304. // When
  305. let request = manager.request(urlRequest)
  306. .resume()
  307. .cancel()
  308. .response { resp in
  309. response = resp
  310. expectation.fulfill()
  311. }
  312. waitForExpectations(timeout: timeout, handler: nil)
  313. // Then
  314. XCTAssertNotNil(response, "response should not be nil")
  315. XCTAssertTrue(request.isCancelled)
  316. XCTAssertNil(request.task)
  317. guard let error = request.error as? AFError, case .explicitlyCancelled = error else { XCTFail(); return }
  318. }
  319. func testSetStartRequestsImmediatelyToFalseAndCancelThenResumeRequestDoesntCreateTaskAndStaysCancelled() {
  320. // Given
  321. let delegate = SessionDelegate(startRequestsImmediately: false)
  322. let manager = SessionManager(delegate: delegate)
  323. let url = URL(string: "https://httpbin.org/get")!
  324. let urlRequest = URLRequest(url: url)
  325. let expectation = self.expectation(description: "\(url)")
  326. var response: DataResponse<Data?>?
  327. // When
  328. let request = manager.request(urlRequest)
  329. .cancel()
  330. .resume()
  331. .response { resp in
  332. response = resp
  333. expectation.fulfill()
  334. }
  335. waitForExpectations(timeout: timeout, handler: nil)
  336. // Then
  337. XCTAssertNotNil(response, "response should not be nil")
  338. XCTAssertTrue(request.isCancelled)
  339. XCTAssertNil(request.task)
  340. guard let error = request.error as? AFError, case .explicitlyCancelled = error else { XCTFail(); return }
  341. }
  342. // MARK: Tests - Deinitialization
  343. func testReleasingManagerWithPendingRequestDeinitializesSuccessfully() {
  344. // Given
  345. let delegate = SessionDelegate(startRequestsImmediately: false)
  346. let monitor = ClosureEventMonitor()
  347. let expectation = self.expectation(description: "Request created")
  348. monitor.requestDidCreateTask = { (_, _) in expectation.fulfill() }
  349. var manager: SessionManager? = SessionManager(delegate: delegate, eventMonitors: [monitor])
  350. let url = URL(string: "https://httpbin.org/get")!
  351. let urlRequest = URLRequest(url: url)
  352. // When
  353. let request = manager?.request(urlRequest)
  354. manager = nil
  355. waitForExpectations(timeout: timeout, handler: nil)
  356. // Then
  357. XCTAssertEqual(request?.task?.state, .suspended)
  358. XCTAssertNil(manager, "manager should be nil")
  359. }
  360. // TODO: Make this test wait less with proper internal async work.
  361. // TODO: Reevaluate this test with new async structure vs. deinit
  362. func testReleasingManagerWithPendingCanceledRequestDeinitializesSuccessfully() {
  363. // Given
  364. let delegate = SessionDelegate(startRequestsImmediately: false)
  365. var manager: SessionManager? = SessionManager(delegate: delegate)
  366. let url = URL(string: "https://httpbin.org/get")!
  367. let urlRequest = URLRequest(url: url)
  368. // When
  369. let request = manager?.request(urlRequest)
  370. request?.cancel()
  371. manager = nil
  372. // Then
  373. let state = request?.state
  374. XCTAssertTrue(state == .cancelled, "state should be .canceling or .completed")
  375. XCTAssertNil(manager, "manager should be nil")
  376. }
  377. // MARK: Tests - Bad Requests
  378. func testThatDataRequestWithInvalidURLStringThrowsResponseHandlerError() {
  379. // Given
  380. let sessionManager = SessionManager()
  381. let expectation = self.expectation(description: "Request should fail with error")
  382. var response: DataResponse<Data?>?
  383. // When
  384. sessionManager.request("https://httpbin.org/get/äëïöü").response { resp in
  385. response = resp
  386. expectation.fulfill()
  387. }
  388. waitForExpectations(timeout: timeout, handler: nil)
  389. // Then
  390. XCTAssertNil(response?.request)
  391. XCTAssertNil(response?.response)
  392. XCTAssertNil(response?.data)
  393. XCTAssertNotNil(response?.error)
  394. if let error = response?.error as? AFError {
  395. XCTAssertTrue(error.isInvalidURLError)
  396. XCTAssertEqual(error.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  397. } else {
  398. XCTFail("error should not be nil")
  399. }
  400. }
  401. // func testThatDownloadRequestWithInvalidURLStringThrowsResponseHandlerError() {
  402. // // Given
  403. // let sessionManager = SessionManager()
  404. // let expectation = self.expectation(description: "Download should fail with error")
  405. //
  406. // var response: DownloadResponse<URL?>?
  407. //
  408. // // When
  409. // sessionManager.download("https://httpbin.org/get/äëïöü").response { resp in
  410. // response = resp
  411. // expectation.fulfill()
  412. // }
  413. //
  414. // waitForExpectations(timeout: timeout, handler: nil)
  415. //
  416. // // Then
  417. // XCTAssertNil(response?.request)
  418. // XCTAssertNil(response?.response)
  419. // XCTAssertNil(response?.temporaryURL)
  420. // XCTAssertNil(response?.destinationURL)
  421. // XCTAssertNil(response?.resumeData)
  422. // XCTAssertNotNil(response?.error)
  423. //
  424. // if let error = response?.error as? AFError {
  425. // XCTAssertTrue(error.isInvalidURLError)
  426. // XCTAssertEqual(error.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  427. // } else {
  428. // XCTFail("error should not be nil")
  429. // }
  430. // }
  431. //
  432. func testThatUploadDataRequestWithInvalidURLStringThrowsResponseHandlerError() {
  433. // Given
  434. let sessionManager = SessionManager()
  435. let expectation = self.expectation(description: "Upload should fail with error")
  436. var response: DataResponse<Data?>?
  437. // When
  438. sessionManager.upload(Data(), to: "https://httpbin.org/get/äëïöü").response { resp in
  439. response = resp
  440. expectation.fulfill()
  441. }
  442. waitForExpectations(timeout: timeout, handler: nil)
  443. // Then
  444. XCTAssertNil(response?.request)
  445. XCTAssertNil(response?.response)
  446. XCTAssertNil(response?.data)
  447. XCTAssertNotNil(response?.error)
  448. if let error = response?.error as? AFError {
  449. XCTAssertTrue(error.isInvalidURLError)
  450. XCTAssertEqual(error.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  451. } else {
  452. XCTFail("error should not be nil")
  453. }
  454. }
  455. func testThatUploadFileRequestWithInvalidURLStringThrowsResponseHandlerError() {
  456. // Given
  457. let sessionManager = SessionManager()
  458. let expectation = self.expectation(description: "Upload should fail with error")
  459. var response: DataResponse<Data?>?
  460. // When
  461. sessionManager.upload(URL(fileURLWithPath: "/invalid"), to: "https://httpbin.org/get/äëïöü").response { resp in
  462. response = resp
  463. expectation.fulfill()
  464. }
  465. waitForExpectations(timeout: timeout, handler: nil)
  466. // Then
  467. XCTAssertNil(response?.request)
  468. XCTAssertNil(response?.response)
  469. XCTAssertNil(response?.data)
  470. XCTAssertNotNil(response?.error)
  471. if let error = response?.error as? AFError {
  472. XCTAssertTrue(error.isInvalidURLError)
  473. XCTAssertEqual(error.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  474. } else {
  475. XCTFail("error should not be nil")
  476. }
  477. }
  478. func testThatUploadStreamRequestWithInvalidURLStringThrowsResponseHandlerError() {
  479. // Given
  480. let sessionManager = SessionManager()
  481. let expectation = self.expectation(description: "Upload should fail with error")
  482. var response: DataResponse<Data?>?
  483. // When
  484. sessionManager.upload(InputStream(data: Data()), to: "https://httpbin.org/get/äëïöü").response { resp in
  485. response = resp
  486. expectation.fulfill()
  487. }
  488. waitForExpectations(timeout: timeout, handler: nil)
  489. // Then
  490. XCTAssertNil(response?.request)
  491. XCTAssertNil(response?.response)
  492. XCTAssertNil(response?.data)
  493. XCTAssertNotNil(response?.error)
  494. if let error = response?.error as? AFError {
  495. XCTAssertTrue(error.isInvalidURLError)
  496. XCTAssertEqual(error.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  497. } else {
  498. XCTFail("error should not be nil")
  499. }
  500. }
  501. // MARK: Tests - Request Adapter
  502. func testThatSessionManagerCallsRequestAdapterWhenCreatingDataRequest() {
  503. // Given
  504. let adapter = HTTPMethodAdapter(method: .post)
  505. let delegate = SessionDelegate(startRequestsImmediately: false)
  506. let monitor = ClosureEventMonitor()
  507. let expectation = self.expectation(description: "Request created")
  508. monitor.requestDidCreateTask = { (_, _) in expectation.fulfill() }
  509. let sessionManager = SessionManager(delegate: delegate, adapter: adapter, eventMonitors: [monitor])
  510. // When
  511. let request = sessionManager.request("https://httpbin.org/get")
  512. waitForExpectations(timeout: timeout, handler: nil)
  513. // Then
  514. XCTAssertEqual(request.task?.originalRequest?.httpMethod, adapter.method.rawValue)
  515. }
  516. func testThatSessionManagerCallsRequestAdapterWhenCreatingDownloadRequest() {
  517. // Given
  518. let adapter = HTTPMethodAdapter(method: .post)
  519. let delegate = SessionDelegate(startRequestsImmediately: false)
  520. let monitor = ClosureEventMonitor()
  521. let expectation = self.expectation(description: "Request created")
  522. monitor.requestDidCreateTask = { (_, _) in expectation.fulfill() }
  523. let sessionManager = SessionManager(delegate: delegate, adapter: adapter, eventMonitors: [monitor])
  524. // When
  525. let request = sessionManager.download("https://httpbin.org/get")
  526. waitForExpectations(timeout: timeout, handler: nil)
  527. // Then
  528. XCTAssertEqual(request.task?.originalRequest?.httpMethod, adapter.method.rawValue)
  529. }
  530. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithData() {
  531. // Given
  532. let adapter = HTTPMethodAdapter(method: .get)
  533. let delegate = SessionDelegate(startRequestsImmediately: false)
  534. let monitor = ClosureEventMonitor()
  535. let expectation = self.expectation(description: "Request created")
  536. monitor.requestDidCreateTask = { (_, _) in expectation.fulfill() }
  537. let sessionManager = SessionManager(delegate: delegate, adapter: adapter, eventMonitors: [monitor])
  538. // When
  539. let request = sessionManager.upload(Data("data".utf8), to: "https://httpbin.org/post")
  540. waitForExpectations(timeout: timeout, handler: nil)
  541. // Then
  542. XCTAssertEqual(request.task?.originalRequest?.httpMethod, adapter.method.rawValue)
  543. }
  544. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithFile() {
  545. // Given
  546. let adapter = HTTPMethodAdapter(method: .get)
  547. let delegate = SessionDelegate(startRequestsImmediately: false)
  548. let monitor = ClosureEventMonitor()
  549. let expectation = self.expectation(description: "Request created")
  550. monitor.requestDidCreateTask = { (_, _) in expectation.fulfill() }
  551. let sessionManager = SessionManager(delegate: delegate, adapter: adapter, eventMonitors: [monitor])
  552. // When
  553. let fileURL = URL(fileURLWithPath: "/path/to/some/file.txt")
  554. let request = sessionManager.upload(fileURL, to: "https://httpbin.org/post")
  555. waitForExpectations(timeout: timeout, handler: nil)
  556. // Then
  557. XCTAssertEqual(request.task?.originalRequest?.httpMethod, adapter.method.rawValue)
  558. }
  559. func testThatSessionManagerCallsRequestAdapterWhenCreatingUploadRequestWithInputStream() {
  560. // Given
  561. let adapter = HTTPMethodAdapter(method: .get)
  562. let delegate = SessionDelegate(startRequestsImmediately: false)
  563. let monitor = ClosureEventMonitor()
  564. let expectation = self.expectation(description: "Request created")
  565. monitor.requestDidCreateTask = { (_, _) in expectation.fulfill() }
  566. let sessionManager = SessionManager(delegate: delegate, adapter: adapter, eventMonitors: [monitor])
  567. // When
  568. let inputStream = InputStream(data: Data("data".utf8))
  569. let request = sessionManager.upload(inputStream, to: "https://httpbin.org/post")
  570. waitForExpectations(timeout: timeout, handler: nil)
  571. // Then
  572. XCTAssertEqual(request.task?.originalRequest?.httpMethod, adapter.method.rawValue)
  573. }
  574. func testThatRequestAdapterErrorThrowsResponseHandlerError() {
  575. // Given
  576. let adapter = HTTPMethodAdapter(method: .post, throwsError: true)
  577. let delegate = SessionDelegate(startRequestsImmediately: false)
  578. let monitor = ClosureEventMonitor()
  579. let expectation = self.expectation(description: "Request created")
  580. monitor.requestDidFailToAdaptURLRequestWithError = { (_, _, _) in expectation.fulfill() }
  581. let sessionManager = SessionManager(delegate: delegate, adapter: adapter, eventMonitors: [monitor])
  582. // When
  583. let request = sessionManager.request("https://httpbin.org/get")
  584. waitForExpectations(timeout: timeout, handler: nil)
  585. // Then
  586. if let error = request.error as? AFError {
  587. XCTAssertTrue(error.isInvalidURLError)
  588. XCTAssertEqual(error.urlConvertible as? String, "")
  589. } else {
  590. XCTFail("error should not be nil")
  591. }
  592. }
  593. // MARK: Tests - Request Retrier
  594. func testThatSessionManagerCallsRequestRetrierWhenRequestEncountersError() {
  595. // Given
  596. let handler = RequestHandler()
  597. let sessionManager = SessionManager(adapter: handler, retrier: handler)
  598. let expectation = self.expectation(description: "request should eventually fail")
  599. var response: DataResponse<Any>?
  600. // When
  601. let request = sessionManager.request("https://httpbin.org/basic-auth/user/password")
  602. .validate()
  603. .responseJSON { jsonResponse in
  604. response = jsonResponse
  605. expectation.fulfill()
  606. }
  607. waitForExpectations(timeout: timeout, handler: nil)
  608. // Then
  609. XCTAssertEqual(handler.adaptedCount, 2)
  610. XCTAssertEqual(handler.retryCount, 2)
  611. XCTAssertEqual(request.retryCount, 1)
  612. XCTAssertEqual(response?.result.isSuccess, false)
  613. XCTAssertTrue(sessionManager.delegate.requestTaskMap.isEmpty)
  614. }
  615. func testThatSessionManagerCallsRequestRetrierWhenRequestInitiallyEncountersAdaptError() {
  616. // Given
  617. let handler = RequestHandler()
  618. handler.adaptedCount = 1
  619. handler.throwsErrorOnSecondAdapt = true
  620. handler.shouldApplyAuthorizationHeader = true
  621. let sessionManager = SessionManager(adapter: handler, retrier: handler)
  622. let expectation = self.expectation(description: "request should eventually fail")
  623. var response: DataResponse<Any>?
  624. // When
  625. sessionManager.request("https://httpbin.org/basic-auth/user/password")
  626. .validate()
  627. .responseJSON { jsonResponse in
  628. response = jsonResponse
  629. expectation.fulfill()
  630. }
  631. waitForExpectations(timeout: timeout, handler: nil)
  632. // Then
  633. XCTAssertEqual(handler.adaptedCount, 2)
  634. XCTAssertEqual(handler.retryCount, 1)
  635. XCTAssertEqual(response?.result.isSuccess, true)
  636. XCTAssertTrue(sessionManager.delegate.requestTaskMap.isEmpty)
  637. }
  638. func testThatSessionManagerCallsRequestRetrierWhenDownloadInitiallyEncountersAdaptError() {
  639. // Given
  640. let handler = RequestHandler()
  641. handler.adaptedCount = 1
  642. handler.throwsErrorOnSecondAdapt = true
  643. handler.shouldApplyAuthorizationHeader = true
  644. let sessionManager = SessionManager(adapter: handler, retrier: handler)
  645. let expectation = self.expectation(description: "request should eventually fail")
  646. var response: DownloadResponse<Any>?
  647. let destination: DownloadRequest.Destination = { _, _ in
  648. let fileURL = self.testDirectoryURL.appendingPathComponent("test-output.json")
  649. return (fileURL, [.removePreviousFile])
  650. }
  651. // When
  652. sessionManager.download("https://httpbin.org/basic-auth/user/password", to: destination)
  653. .validate()
  654. .responseJSON { jsonResponse in
  655. response = jsonResponse
  656. expectation.fulfill()
  657. }
  658. waitForExpectations(timeout: timeout, handler: nil)
  659. // Then
  660. XCTAssertEqual(handler.adaptedCount, 2)
  661. XCTAssertEqual(handler.retryCount, 1)
  662. XCTAssertEqual(response?.result.isSuccess, true)
  663. XCTAssertTrue(sessionManager.delegate.requestTaskMap.isEmpty)
  664. }
  665. func testThatSessionManagerCallsRequestRetrierWhenUploadInitiallyEncountersAdaptError() {
  666. // Given
  667. let handler = UploadHandler()
  668. let sessionManager = SessionManager(adapter: handler, retrier: handler)
  669. let expectation = self.expectation(description: "request should eventually fail")
  670. var response: DataResponse<Any>?
  671. let uploadData = Data("upload data".utf8)
  672. // When
  673. sessionManager.upload(uploadData, to: "https://httpbin.org/post")
  674. .validate()
  675. .responseJSON { jsonResponse in
  676. response = jsonResponse
  677. expectation.fulfill()
  678. }
  679. waitForExpectations(timeout: timeout, handler: nil)
  680. // Then
  681. XCTAssertEqual(handler.adaptedCount, 2)
  682. XCTAssertEqual(handler.retryCount, 1)
  683. XCTAssertEqual(response?.result.isSuccess, true)
  684. XCTAssertTrue(sessionManager.delegate.requestTaskMap.isEmpty)
  685. }
  686. func testThatSessionManagerCallsAdapterWhenRequestIsRetried() {
  687. // Given
  688. let handler = RequestHandler()
  689. handler.shouldApplyAuthorizationHeader = true
  690. let sessionManager = SessionManager(adapter: handler, retrier: handler)
  691. let expectation = self.expectation(description: "request should eventually succeed")
  692. var response: DataResponse<Any>?
  693. // When
  694. let request = sessionManager.request("https://httpbin.org/basic-auth/user/password")
  695. .validate()
  696. .responseJSON { jsonResponse in
  697. response = jsonResponse
  698. expectation.fulfill()
  699. }
  700. waitForExpectations(timeout: timeout, handler: nil)
  701. // Then
  702. XCTAssertEqual(handler.adaptedCount, 2)
  703. XCTAssertEqual(handler.retryCount, 1)
  704. XCTAssertEqual(request.retryCount, 1)
  705. XCTAssertEqual(response?.result.isSuccess, true)
  706. XCTAssertTrue(sessionManager.delegate.requestTaskMap.isEmpty)
  707. }
  708. // TODO: Confirm retry logic.
  709. // func testThatRequestAdapterErrorThrowsResponseHandlerErrorWhenRequestIsRetried() {
  710. // // Given
  711. // let handler = RequestHandler()
  712. // handler.throwsErrorOnSecondAdapt = true
  713. //
  714. // let sessionManager = SessionManager(adapter: handler, retrier: handler)
  715. //
  716. // let expectation = self.expectation(description: "request should eventually fail")
  717. // var response: DataResponse<Any>?
  718. //
  719. // // When
  720. // let request = sessionManager.request("https://httpbin.org/basic-auth/user/password")
  721. // .validate()
  722. // .responseJSON { jsonResponse in
  723. // response = jsonResponse
  724. // expectation.fulfill()
  725. // }
  726. //
  727. // waitForExpectations(timeout: timeout, handler: nil)
  728. //
  729. // // Then
  730. // XCTAssertEqual(handler.adaptedCount, 1)
  731. // XCTAssertEqual(handler.retryCount, 1)
  732. // XCTAssertEqual(request.retryCount, 0)
  733. // XCTAssertEqual(response?.result.isSuccess, false)
  734. // XCTAssertTrue(sessionManager.delegate.requestTaskMap.isEmpty)
  735. //
  736. // if let error = response?.result.error as? AFError {
  737. // XCTAssertTrue(error.isInvalidURLError)
  738. // XCTAssertEqual(error.urlConvertible as? String, "")
  739. // } else {
  740. // XCTFail("error should not be nil")
  741. // }
  742. // }
  743. }
  744. // MARK: -
  745. class SessionManagerConfigurationHeadersTestCase: BaseTestCase {
  746. enum ConfigurationType {
  747. case `default`, ephemeral, background
  748. }
  749. func testThatDefaultConfigurationHeadersAreSentWithRequest() {
  750. // Given, When, Then
  751. executeAuthorizationHeaderTest(for: .default)
  752. }
  753. func testThatEphemeralConfigurationHeadersAreSentWithRequest() {
  754. // Given, When, Then
  755. executeAuthorizationHeaderTest(for: .ephemeral)
  756. }
  757. #if os(macOS)
  758. func testThatBackgroundConfigurationHeadersAreSentWithRequest() {
  759. // Given, When, Then
  760. executeAuthorizationHeaderTest(for: .background)
  761. }
  762. #endif
  763. private func executeAuthorizationHeaderTest(for type: ConfigurationType) {
  764. // Given
  765. let manager: SessionManager = {
  766. let configuration: URLSessionConfiguration = {
  767. let configuration: URLSessionConfiguration
  768. switch type {
  769. case .default:
  770. configuration = .default
  771. case .ephemeral:
  772. configuration = .ephemeral
  773. case .background:
  774. let identifier = "org.alamofire.test.manager-configuration-tests"
  775. configuration = .background(withIdentifier: identifier)
  776. }
  777. var headers = SessionManager.defaultHTTPHeaders
  778. headers["Authorization"] = "Bearer 123456"
  779. configuration.httpAdditionalHeaders = headers
  780. return configuration
  781. }()
  782. return SessionManager(configuration: configuration)
  783. }()
  784. let expectation = self.expectation(description: "request should complete successfully")
  785. var response: DataResponse<Any>?
  786. // When
  787. manager.request("https://httpbin.org/headers")
  788. .responseJSON { closureResponse in
  789. response = closureResponse
  790. expectation.fulfill()
  791. }
  792. waitForExpectations(timeout: timeout, handler: nil)
  793. // Then
  794. if let response = response {
  795. XCTAssertNotNil(response.request, "request should not be nil")
  796. XCTAssertNotNil(response.response, "response should not be nil")
  797. XCTAssertNotNil(response.data, "data should not be nil")
  798. XCTAssertTrue(response.result.isSuccess, "result should be a success")
  799. if
  800. let response = response.result.value as? [String: Any],
  801. let headers = response["headers"] as? [String: String],
  802. let authorization = headers["Authorization"]
  803. {
  804. XCTAssertEqual(authorization, "Bearer 123456", "authorization header value does not match")
  805. } else {
  806. XCTFail("failed to extract authorization header value")
  807. }
  808. } else {
  809. XCTFail("response should not be nil")
  810. }
  811. }
  812. }