SessionTests.swift 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737
  1. //
  2. // SessionTests.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. final class SessionTestCase: BaseTestCase {
  28. // MARK: Helper Types
  29. private class HTTPMethodAdapter: RequestInterceptor {
  30. let method: HTTPMethod
  31. let throwsError: Bool
  32. var adaptedCount = 0
  33. init(method: HTTPMethod, throwsError: Bool = false) {
  34. self.method = method
  35. self.throwsError = throwsError
  36. }
  37. func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
  38. adaptedCount += 1
  39. let result: Result<URLRequest, Error> = Result {
  40. guard !throwsError else { throw AFError.invalidURL(url: "") }
  41. var urlRequest = urlRequest
  42. urlRequest.httpMethod = method.rawValue
  43. return urlRequest
  44. }
  45. completion(result)
  46. }
  47. }
  48. private class HeaderAdapter: RequestInterceptor {
  49. let headers: HTTPHeaders
  50. let throwsError: Bool
  51. var adaptedCount = 0
  52. init(headers: HTTPHeaders = ["field": "value"], throwsError: Bool = false) {
  53. self.headers = headers
  54. self.throwsError = throwsError
  55. }
  56. func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
  57. adaptedCount += 1
  58. let result: Result<URLRequest, Error> = Result {
  59. guard !throwsError else { throw AFError.invalidURL(url: "") }
  60. var urlRequest = urlRequest
  61. var finalHeaders = urlRequest.headers
  62. headers.forEach { finalHeaders.add($0) }
  63. urlRequest.headers = finalHeaders
  64. return urlRequest
  65. }
  66. completion(result)
  67. }
  68. }
  69. private class RequestHandler: RequestInterceptor {
  70. var adaptCalledCount = 0
  71. var adaptedCount = 0
  72. var retryCount = 0
  73. var retryCalledCount = 0
  74. var retryErrors: [Error] = []
  75. var shouldApplyAuthorizationHeader = false
  76. var throwsErrorOnFirstAdapt = false
  77. var throwsErrorOnSecondAdapt = false
  78. var throwsErrorOnRetry = false
  79. var shouldRetry = true
  80. var retryDelay: TimeInterval?
  81. func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
  82. adaptCalledCount += 1
  83. let result: Result<URLRequest, Error> = Result {
  84. if throwsErrorOnFirstAdapt {
  85. throwsErrorOnFirstAdapt = false
  86. throw AFError.invalidURL(url: "/adapt/error/1")
  87. }
  88. if throwsErrorOnSecondAdapt && adaptedCount == 1 {
  89. throwsErrorOnSecondAdapt = false
  90. throw AFError.invalidURL(url: "/adapt/error/2")
  91. }
  92. var urlRequest = urlRequest
  93. adaptedCount += 1
  94. if shouldApplyAuthorizationHeader && adaptedCount > 1 {
  95. urlRequest.headers.update(.authorization(username: "user", password: "password"))
  96. }
  97. return urlRequest
  98. }
  99. completion(result)
  100. }
  101. func retry(
  102. _ request: Request,
  103. for session: Session,
  104. dueTo error: Error,
  105. completion: @escaping (RetryResult) -> Void)
  106. {
  107. retryCalledCount += 1
  108. if throwsErrorOnRetry {
  109. let error = AFError.invalidURL(url: "/invalid/url/\(retryCalledCount)")
  110. completion(.doNotRetryWithError(error))
  111. return
  112. }
  113. guard shouldRetry else { completion(.doNotRetry); return }
  114. retryCount += 1
  115. retryErrors.append(error)
  116. if retryCount < 2 {
  117. if let retryDelay = retryDelay {
  118. completion(.retryWithDelay(retryDelay))
  119. } else {
  120. completion(.retry)
  121. }
  122. } else {
  123. completion(.doNotRetry)
  124. }
  125. }
  126. }
  127. private class UploadHandler: RequestInterceptor {
  128. var adaptCalledCount = 0
  129. var adaptedCount = 0
  130. var retryCalledCount = 0
  131. var retryCount = 0
  132. var retryErrors: [Error] = []
  133. func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
  134. adaptCalledCount += 1
  135. let result: Result<URLRequest, Error> = Result {
  136. adaptedCount += 1
  137. if adaptedCount == 1 { throw AFError.invalidURL(url: "") }
  138. return urlRequest
  139. }
  140. completion(result)
  141. }
  142. func retry(
  143. _ request: Request,
  144. for session: Session,
  145. dueTo error: Error,
  146. completion: @escaping (RetryResult) -> Void)
  147. {
  148. retryCalledCount += 1
  149. retryCount += 1
  150. retryErrors.append(error)
  151. completion(.retry)
  152. }
  153. }
  154. // MARK: Tests - Initialization
  155. func testInitializerWithDefaultArguments() {
  156. // Given, When
  157. let session = Session()
  158. // Then
  159. XCTAssertNotNil(session.session.delegate, "session delegate should not be nil")
  160. XCTAssertTrue(session.delegate === session.session.delegate, "manager delegate should equal session delegate")
  161. XCTAssertNil(session.serverTrustManager, "session server trust policy manager should be nil")
  162. }
  163. func testInitializerWithSpecifiedArguments() {
  164. // Given
  165. let configuration = URLSessionConfiguration.default
  166. let delegate = SessionDelegate()
  167. let serverTrustManager = ServerTrustManager(evaluators: [:])
  168. // When
  169. let session = Session(configuration: configuration,
  170. delegate: delegate,
  171. serverTrustManager: serverTrustManager)
  172. // Then
  173. XCTAssertNotNil(session.session.delegate, "session delegate should not be nil")
  174. XCTAssertTrue(session.delegate === session.session.delegate, "manager delegate should equal session delegate")
  175. XCTAssertNotNil(session.serverTrustManager, "session server trust policy manager should not be nil")
  176. }
  177. func testThatSessionInitializerSucceedsWithDefaultArguments() {
  178. // Given
  179. let delegate = SessionDelegate()
  180. let underlyingQueue = DispatchQueue(label: "underlyingQueue")
  181. let urlSession: URLSession = {
  182. let configuration = URLSessionConfiguration.default
  183. let queue = OperationQueue(underlyingQueue: underlyingQueue, name: "delegateQueue")
  184. return URLSession(configuration: configuration, delegate: delegate, delegateQueue: queue)
  185. }()
  186. // When
  187. let session = Session(session: urlSession, delegate: delegate, rootQueue: underlyingQueue)
  188. // Then
  189. XCTAssertTrue(session.delegate === session.session.delegate, "manager delegate should equal session delegate")
  190. XCTAssertNil(session.serverTrustManager, "session server trust policy manager should be nil")
  191. }
  192. func testThatSessionInitializerSucceedsWithSpecifiedArguments() {
  193. // Given
  194. let delegate = SessionDelegate()
  195. let underlyingQueue = DispatchQueue(label: "underlyingQueue")
  196. let urlSession: URLSession = {
  197. let configuration = URLSessionConfiguration.default
  198. let queue = OperationQueue(underlyingQueue: underlyingQueue, name: "delegateQueue")
  199. return URLSession(configuration: configuration, delegate: delegate, delegateQueue: queue)
  200. }()
  201. let serverTrustManager = ServerTrustManager(evaluators: [:])
  202. // When
  203. let session = Session(session: urlSession,
  204. delegate: delegate,
  205. rootQueue: underlyingQueue,
  206. serverTrustManager: serverTrustManager)
  207. // Then
  208. XCTAssertTrue(session.delegate === session.session.delegate, "manager delegate should equal session delegate")
  209. XCTAssertNotNil(session.serverTrustManager, "session server trust policy manager should not be nil")
  210. }
  211. // MARK: Tests - Default HTTP Headers
  212. func testDefaultUserAgentHeader() {
  213. // Given, When
  214. let userAgent = HTTPHeaders.default["User-Agent"]
  215. // Then
  216. let osNameVersion: String = {
  217. let version = ProcessInfo.processInfo.operatingSystemVersion
  218. let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
  219. let osName: String = {
  220. #if os(iOS)
  221. return "iOS"
  222. #elseif os(watchOS)
  223. return "watchOS"
  224. #elseif os(tvOS)
  225. return "tvOS"
  226. #elseif os(macOS)
  227. return "macOS"
  228. #elseif os(Linux)
  229. return "Linux"
  230. #else
  231. return "Unknown"
  232. #endif
  233. }()
  234. return "\(osName) \(versionString)"
  235. }()
  236. let alamofireVersion: String = {
  237. guard
  238. let afInfo = Bundle(for: Session.self).infoDictionary,
  239. let build = afInfo["CFBundleShortVersionString"]
  240. else { return "Unknown" }
  241. return "Alamofire/\(build)"
  242. }()
  243. XCTAssertTrue(userAgent?.contains(alamofireVersion) == true)
  244. XCTAssertTrue(userAgent?.contains(osNameVersion) == true)
  245. XCTAssertTrue(userAgent?.contains("Unknown/Unknown") == true)
  246. }
  247. // MARK: Tests - Supported Accept-Encodings
  248. func testDefaultAcceptEncodingSupportsAppropriateEncodingsOnAppropriateSystems() {
  249. // Given
  250. let brotliURL = URL(string: "https://httpbin.org/brotli")!
  251. let gzipURL = URL(string: "https://httpbin.org/gzip")!
  252. let deflateURL = URL(string: "https://httpbin.org/deflate")!
  253. let brotliExpectation = expectation(description: "brotli request should complete")
  254. let gzipExpectation = expectation(description: "gzip request should complete")
  255. let deflateExpectation = expectation(description: "deflate request should complete")
  256. var brotliResponse: DataResponse<Any>?
  257. var gzipResponse: DataResponse<Any>?
  258. var deflateResponse: DataResponse<Any>?
  259. // When
  260. AF.request(brotliURL).responseJSON { response in
  261. brotliResponse = response
  262. brotliExpectation.fulfill()
  263. }
  264. AF.request(gzipURL).responseJSON { response in
  265. gzipResponse = response
  266. gzipExpectation.fulfill()
  267. }
  268. AF.request(deflateURL).responseJSON { response in
  269. deflateResponse = response
  270. deflateExpectation.fulfill()
  271. }
  272. waitForExpectations(timeout: timeout, handler: nil)
  273. // Then
  274. if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) {
  275. XCTAssertTrue(brotliResponse?.result.isSuccess == true)
  276. } else {
  277. XCTAssertTrue(brotliResponse?.result.isFailure == true)
  278. }
  279. XCTAssertTrue(gzipResponse?.result.isSuccess == true)
  280. XCTAssertTrue(deflateResponse?.result.isSuccess == true)
  281. }
  282. // MARK: Tests - Start Requests Immediately
  283. func testSetStartRequestsImmediatelyToFalseAndResumeRequest() {
  284. // Given
  285. let session = Session(startRequestsImmediately: false)
  286. let url = URL(string: "https://httpbin.org/get")!
  287. let urlRequest = URLRequest(url: url)
  288. let expectation = self.expectation(description: "\(url)")
  289. var response: HTTPURLResponse?
  290. // When
  291. session.request(urlRequest)
  292. .response { resp in
  293. response = resp.response
  294. expectation.fulfill()
  295. }
  296. .resume()
  297. waitForExpectations(timeout: timeout, handler: nil)
  298. // Then
  299. XCTAssertNotNil(response, "response should not be nil")
  300. XCTAssertTrue(response?.statusCode == 200, "response status code should be 200")
  301. }
  302. func testSetStartRequestsImmediatelyToFalseAndCancelledCallsResponseHandlers() {
  303. // Given
  304. let session = Session(startRequestsImmediately: false)
  305. let url = URL(string: "https://httpbin.org/get")!
  306. let urlRequest = URLRequest(url: url)
  307. let expectation = self.expectation(description: "\(url)")
  308. var response: DataResponse<Data?>?
  309. // When
  310. let request = session.request(urlRequest)
  311. .cancel()
  312. .response { resp in
  313. response = resp
  314. expectation.fulfill()
  315. }
  316. waitForExpectations(timeout: timeout, handler: nil)
  317. // Then
  318. XCTAssertNotNil(response, "response should not be nil")
  319. XCTAssertTrue(request.isCancelled)
  320. XCTAssertTrue((request.task == nil) || (request.task?.state == .canceling || request.task?.state == .completed))
  321. guard let error = request.error?.asAFError, case .explicitlyCancelled = error else {
  322. XCTFail("Request should have an .explicitlyCancelled error.")
  323. return
  324. }
  325. }
  326. func testSetStartRequestsImmediatelyToFalseAndResumeThenCancelRequestHasCorrectOutput() {
  327. // Given
  328. let session = Session(startRequestsImmediately: false)
  329. let url = URL(string: "https://httpbin.org/get")!
  330. let urlRequest = URLRequest(url: url)
  331. let expectation = self.expectation(description: "\(url)")
  332. var response: DataResponse<Data?>?
  333. // When
  334. let request = session.request(urlRequest)
  335. .resume()
  336. .cancel()
  337. .response { resp in
  338. response = resp
  339. expectation.fulfill()
  340. }
  341. waitForExpectations(timeout: timeout, handler: nil)
  342. // Then
  343. XCTAssertNotNil(response, "response should not be nil")
  344. XCTAssertTrue(request.isCancelled)
  345. XCTAssertTrue((request.task == nil) || (request.task?.state == .canceling || request.task?.state == .completed))
  346. guard let error = request.error?.asAFError, case .explicitlyCancelled = error else {
  347. XCTFail("Request should have an .explicitlyCancelled error.")
  348. return
  349. }
  350. }
  351. func testSetStartRequestsImmediatelyToFalseAndCancelThenResumeRequestDoesntCreateTaskAndStaysCancelled() {
  352. // Given
  353. let session = Session(startRequestsImmediately: false)
  354. let url = URL(string: "https://httpbin.org/get")!
  355. let urlRequest = URLRequest(url: url)
  356. let expectation = self.expectation(description: "\(url)")
  357. var response: DataResponse<Data?>?
  358. // When
  359. let request = session.request(urlRequest)
  360. .cancel()
  361. .resume()
  362. .response { resp in
  363. response = resp
  364. expectation.fulfill()
  365. }
  366. waitForExpectations(timeout: timeout, handler: nil)
  367. // Then
  368. XCTAssertNotNil(response, "response should not be nil")
  369. XCTAssertTrue(request.isCancelled)
  370. XCTAssertTrue((request.task == nil) || (request.task?.state == .canceling || request.task?.state == .completed))
  371. guard let error = request.error?.asAFError, case .explicitlyCancelled = error else {
  372. XCTFail("Request should have an .explicitlyCancelled error.")
  373. return
  374. }
  375. }
  376. // MARK: Tests - Deinitialization
  377. func testReleasingManagerWithPendingRequestDeinitializesSuccessfully() {
  378. // Given
  379. let monitor = ClosureEventMonitor()
  380. let expectation = self.expectation(description: "Request created")
  381. monitor.requestDidCreateTask = { _, _ in expectation.fulfill() }
  382. var session: Session? = Session(startRequestsImmediately: false, eventMonitors: [monitor])
  383. let url = URL(string: "https://httpbin.org/get")!
  384. let urlRequest = URLRequest(url: url)
  385. // When
  386. let request = session?.request(urlRequest)
  387. session = nil
  388. waitForExpectations(timeout: timeout, handler: nil)
  389. // Then
  390. XCTAssertEqual(request?.task?.state, .suspended)
  391. XCTAssertNil(session, "manager should be nil")
  392. }
  393. func testReleasingManagerWithPendingCanceledRequestDeinitializesSuccessfully() {
  394. // Given
  395. var session: Session? = Session(startRequestsImmediately: false)
  396. let url = URL(string: "https://httpbin.org/get")!
  397. let urlRequest = URLRequest(url: url)
  398. // When
  399. let request = session?.request(urlRequest)
  400. request?.cancel()
  401. session = nil
  402. let state = request?.state
  403. // Then
  404. XCTAssertTrue(state == .cancelled, "state should be .cancelled")
  405. XCTAssertNil(session, "manager should be nil")
  406. }
  407. // MARK: Tests - Bad Requests
  408. func testThatDataRequestWithInvalidURLStringThrowsResponseHandlerError() {
  409. // Given
  410. let session = Session()
  411. let expectation = self.expectation(description: "Request should fail with error")
  412. var response: DataResponse<Data?>?
  413. // When
  414. session.request("https://httpbin.org/get/äëïöü").response { resp in
  415. response = resp
  416. expectation.fulfill()
  417. }
  418. waitForExpectations(timeout: timeout, handler: nil)
  419. // Then
  420. XCTAssertNil(response?.request)
  421. XCTAssertNil(response?.response)
  422. XCTAssertNil(response?.data)
  423. XCTAssertNotNil(response?.error)
  424. if let error = response?.error?.asAFError {
  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. func testThatDownloadRequestWithInvalidURLStringThrowsResponseHandlerError() {
  432. // Given
  433. let session = Session()
  434. let expectation = self.expectation(description: "Download should fail with error")
  435. var response: DownloadResponse<URL?>?
  436. // When
  437. session.download("https://httpbin.org/get/äëïöü").response { resp in
  438. response = resp
  439. expectation.fulfill()
  440. }
  441. waitForExpectations(timeout: timeout, handler: nil)
  442. // Then
  443. XCTAssertNil(response?.request)
  444. XCTAssertNil(response?.response)
  445. XCTAssertNil(response?.fileURL)
  446. XCTAssertNil(response?.resumeData)
  447. XCTAssertNotNil(response?.error)
  448. if let error = response?.error?.asAFError {
  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 testThatUploadDataRequestWithInvalidURLStringThrowsResponseHandlerError() {
  456. // Given
  457. let session = Session()
  458. let expectation = self.expectation(description: "Upload should fail with error")
  459. var response: DataResponse<Data?>?
  460. // When
  461. session.upload(Data(), 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?.asAFError {
  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 testThatUploadFileRequestWithInvalidURLStringThrowsResponseHandlerError() {
  479. // Given
  480. let session = Session()
  481. let expectation = self.expectation(description: "Upload should fail with error")
  482. var response: DataResponse<Data?>?
  483. // When
  484. session.upload(URL(fileURLWithPath: "/invalid"), 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?.asAFError {
  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. func testThatUploadStreamRequestWithInvalidURLStringThrowsResponseHandlerError() {
  502. // Given
  503. let session = Session()
  504. let expectation = self.expectation(description: "Upload should fail with error")
  505. var response: DataResponse<Data?>?
  506. // When
  507. session.upload(InputStream(data: Data()), to: "https://httpbin.org/get/äëïöü").response { resp in
  508. response = resp
  509. expectation.fulfill()
  510. }
  511. waitForExpectations(timeout: timeout, handler: nil)
  512. // Then
  513. XCTAssertNil(response?.request)
  514. XCTAssertNil(response?.response)
  515. XCTAssertNil(response?.data)
  516. XCTAssertNotNil(response?.error)
  517. if let error = response?.error?.asAFError {
  518. XCTAssertTrue(error.isInvalidURLError)
  519. XCTAssertEqual(error.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  520. } else {
  521. XCTFail("error should not be nil")
  522. }
  523. }
  524. // MARK: Tests - Request Adapter
  525. func testThatSessionCallsRequestAdaptersWhenCreatingDataRequest() {
  526. // Given
  527. let urlString = "https://httpbin.org/get"
  528. let methodAdapter = HTTPMethodAdapter(method: .post)
  529. let headerAdapter = HeaderAdapter()
  530. let monitor = ClosureEventMonitor()
  531. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  532. // When
  533. let expectation1 = self.expectation(description: "Request 1 created")
  534. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  535. let request1 = session.request(urlString)
  536. waitForExpectations(timeout: timeout, handler: nil)
  537. let expectation2 = self.expectation(description: "Request 2 created")
  538. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  539. let request2 = session.request(urlString, interceptor: headerAdapter)
  540. waitForExpectations(timeout: timeout, handler: nil)
  541. // Then
  542. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  543. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  544. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  545. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  546. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  547. }
  548. func testThatSessionCallsRequestAdaptersWhenCreatingDownloadRequest() {
  549. // Given
  550. let urlString = "https://httpbin.org/get"
  551. let methodAdapter = HTTPMethodAdapter(method: .post)
  552. let headerAdapter = HeaderAdapter()
  553. let monitor = ClosureEventMonitor()
  554. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  555. // When
  556. let expectation1 = self.expectation(description: "Request 1 created")
  557. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  558. let request1 = session.download(urlString)
  559. waitForExpectations(timeout: timeout, handler: nil)
  560. let expectation2 = self.expectation(description: "Request 2 created")
  561. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  562. let request2 = session.download(urlString, interceptor: headerAdapter)
  563. waitForExpectations(timeout: timeout, handler: nil)
  564. // Then
  565. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  566. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  567. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  568. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  569. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  570. }
  571. func testThatSessionCallsRequestAdaptersWhenCreatingUploadRequestWithData() {
  572. // Given
  573. let data = Data("data".utf8)
  574. let urlString = "https://httpbin.org/post"
  575. let methodAdapter = HTTPMethodAdapter(method: .get)
  576. let headerAdapter = HeaderAdapter()
  577. let monitor = ClosureEventMonitor()
  578. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  579. // When
  580. let expectation1 = self.expectation(description: "Request 1 created")
  581. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  582. let request1 = session.upload(data, to: urlString)
  583. waitForExpectations(timeout: timeout, handler: nil)
  584. let expectation2 = self.expectation(description: "Request 2 created")
  585. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  586. let request2 = session.upload(data, to: urlString, interceptor: headerAdapter)
  587. waitForExpectations(timeout: timeout, handler: nil)
  588. // Then
  589. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  590. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  591. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  592. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  593. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  594. }
  595. func testThatSessionCallsRequestAdaptersWhenCreatingUploadRequestWithFile() {
  596. // Given
  597. let fileURL = URL(fileURLWithPath: "/path/to/some/file.txt")
  598. let urlString = "https://httpbin.org/post"
  599. let methodAdapter = HTTPMethodAdapter(method: .get)
  600. let headerAdapter = HeaderAdapter()
  601. let monitor = ClosureEventMonitor()
  602. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  603. // When
  604. let expectation1 = self.expectation(description: "Request 1 created")
  605. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  606. let request1 = session.upload(fileURL, to: urlString)
  607. waitForExpectations(timeout: timeout, handler: nil)
  608. let expectation2 = self.expectation(description: "Request 2 created")
  609. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  610. let request2 = session.upload(fileURL, to: urlString, interceptor: headerAdapter)
  611. waitForExpectations(timeout: timeout, handler: nil)
  612. // Then
  613. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  614. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  615. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  616. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  617. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  618. }
  619. func testThatSessionCallsRequestAdaptersWhenCreatingUploadRequestWithInputStream() {
  620. // Given
  621. let inputStream = InputStream(data: Data("data".utf8))
  622. let urlString = "https://httpbin.org/post"
  623. let methodAdapter = HTTPMethodAdapter(method: .get)
  624. let headerAdapter = HeaderAdapter()
  625. let monitor = ClosureEventMonitor()
  626. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  627. // When
  628. let expectation1 = self.expectation(description: "Request 1 created")
  629. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  630. let request1 = session.upload(inputStream, to: urlString)
  631. waitForExpectations(timeout: timeout, handler: nil)
  632. let expectation2 = self.expectation(description: "Request 2 created")
  633. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  634. let request2 = session.upload(inputStream, to: urlString, interceptor: headerAdapter)
  635. waitForExpectations(timeout: timeout, handler: nil)
  636. // Then
  637. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  638. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  639. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  640. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  641. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  642. }
  643. func testThatSessionReturnsRequestAdaptationErrorWhenRequestAdapterThrowsError() {
  644. // Given
  645. let urlString = "https://httpbin.org/get"
  646. let methodAdapter = HTTPMethodAdapter(method: .post, throwsError: true)
  647. let headerAdapter = HeaderAdapter(throwsError: true)
  648. let monitor = ClosureEventMonitor()
  649. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  650. // When
  651. let expectation1 = self.expectation(description: "Request 1 created")
  652. monitor.requestDidFailToAdaptURLRequestWithError = { _, _, _ in expectation1.fulfill() }
  653. let request1 = session.request(urlString)
  654. waitForExpectations(timeout: timeout, handler: nil)
  655. let expectation2 = self.expectation(description: "Request 2 created")
  656. monitor.requestDidFailToAdaptURLRequestWithError = { _, _, _ in expectation2.fulfill() }
  657. let request2 = session.request(urlString, interceptor: headerAdapter)
  658. waitForExpectations(timeout: timeout, handler: nil)
  659. let requests = [request1, request2]
  660. // Then
  661. for request in requests {
  662. if let error = request.error?.asAFError {
  663. XCTAssertTrue(error.isRequestAdaptationError)
  664. XCTAssertEqual(error.underlyingError?.asAFError?.urlConvertible as? String, "")
  665. } else {
  666. XCTFail("error should not be nil")
  667. }
  668. }
  669. }
  670. // MARK: Tests - Request Retrier
  671. func testThatSessionCallsRequestRetrierWhenRequestEncountersError() {
  672. // Given
  673. let handler = RequestHandler()
  674. let session = Session()
  675. let expectation = self.expectation(description: "request should eventually fail")
  676. var response: DataResponse<Any>?
  677. // When
  678. let request = session.request("https://httpbin.org/basic-auth/user/password", interceptor: handler)
  679. .validate()
  680. .responseJSON { jsonResponse in
  681. response = jsonResponse
  682. expectation.fulfill()
  683. }
  684. waitForExpectations(timeout: timeout, handler: nil)
  685. // Then
  686. XCTAssertEqual(handler.adaptCalledCount, 2)
  687. XCTAssertEqual(handler.adaptedCount, 2)
  688. XCTAssertEqual(handler.retryCalledCount, 3)
  689. XCTAssertEqual(handler.retryCount, 3)
  690. XCTAssertEqual(request.retryCount, 1)
  691. XCTAssertEqual(response?.result.isSuccess, false)
  692. assert(on: session.rootQueue) {
  693. XCTAssertTrue(session.requestTaskMap.isEmpty)
  694. XCTAssertTrue(session.activeRequests.isEmpty)
  695. }
  696. }
  697. func testThatSessionCallsRequestRetrierThenSessionRetrierWhenRequestEncountersError() {
  698. // Given
  699. let sessionHandler = RequestHandler()
  700. let requestHandler = RequestHandler()
  701. let session = Session(interceptor: sessionHandler)
  702. let expectation = self.expectation(description: "request should eventually fail")
  703. var response: DataResponse<Any>?
  704. // When
  705. let request = session.request("https://httpbin.org/basic-auth/user/password", interceptor: requestHandler)
  706. .validate()
  707. .responseJSON { jsonResponse in
  708. response = jsonResponse
  709. expectation.fulfill()
  710. }
  711. waitForExpectations(timeout: timeout, handler: nil)
  712. // Then
  713. XCTAssertEqual(sessionHandler.adaptCalledCount, 3)
  714. XCTAssertEqual(sessionHandler.adaptedCount, 3)
  715. XCTAssertEqual(sessionHandler.retryCalledCount, 3)
  716. XCTAssertEqual(sessionHandler.retryCount, 3)
  717. XCTAssertEqual(requestHandler.adaptCalledCount, 3)
  718. XCTAssertEqual(requestHandler.adaptedCount, 3)
  719. XCTAssertEqual(requestHandler.retryCalledCount, 4)
  720. XCTAssertEqual(requestHandler.retryCount, 4)
  721. XCTAssertEqual(request.retryCount, 2)
  722. XCTAssertEqual(response?.result.isSuccess, false)
  723. assert(on: session.rootQueue) {
  724. XCTAssertTrue(session.requestTaskMap.isEmpty)
  725. XCTAssertTrue(session.activeRequests.isEmpty)
  726. }
  727. }
  728. func testThatSessionCallsRequestRetrierWhenRequestInitiallyEncountersAdaptError() {
  729. // Given
  730. let handler = RequestHandler()
  731. handler.adaptedCount = 1
  732. handler.throwsErrorOnSecondAdapt = true
  733. handler.shouldApplyAuthorizationHeader = true
  734. let session = Session()
  735. let expectation = self.expectation(description: "request should eventually fail")
  736. var response: DataResponse<Any>?
  737. // When
  738. session.request("https://httpbin.org/basic-auth/user/password", interceptor: handler)
  739. .validate()
  740. .responseJSON { jsonResponse in
  741. response = jsonResponse
  742. expectation.fulfill()
  743. }
  744. waitForExpectations(timeout: timeout, handler: nil)
  745. // Then
  746. XCTAssertEqual(handler.adaptCalledCount, 2)
  747. XCTAssertEqual(handler.adaptedCount, 2)
  748. XCTAssertEqual(handler.retryCalledCount, 1)
  749. XCTAssertEqual(handler.retryCount, 1)
  750. XCTAssertEqual(response?.result.isSuccess, true)
  751. assert(on: session.rootQueue) {
  752. XCTAssertTrue(session.requestTaskMap.isEmpty)
  753. XCTAssertTrue(session.activeRequests.isEmpty)
  754. }
  755. }
  756. func testThatSessionCallsRequestRetrierWhenDownloadInitiallyEncountersAdaptError() {
  757. // Given
  758. let handler = RequestHandler()
  759. handler.adaptedCount = 1
  760. handler.throwsErrorOnSecondAdapt = true
  761. handler.shouldApplyAuthorizationHeader = true
  762. let session = Session()
  763. let expectation = self.expectation(description: "request should eventually fail")
  764. var response: DownloadResponse<Any>?
  765. let destination: DownloadRequest.Destination = { _, _ in
  766. let fileURL = self.testDirectoryURL.appendingPathComponent("test-output.json")
  767. return (fileURL, [.removePreviousFile])
  768. }
  769. // When
  770. session.download("https://httpbin.org/basic-auth/user/password", interceptor: handler, to: destination)
  771. .validate()
  772. .responseJSON { jsonResponse in
  773. response = jsonResponse
  774. expectation.fulfill()
  775. }
  776. waitForExpectations(timeout: timeout, handler: nil)
  777. // Then
  778. XCTAssertEqual(handler.adaptCalledCount, 2)
  779. XCTAssertEqual(handler.adaptedCount, 2)
  780. XCTAssertEqual(handler.retryCalledCount, 1)
  781. XCTAssertEqual(handler.retryCount, 1)
  782. XCTAssertEqual(response?.result.isSuccess, true)
  783. assert(on: session.rootQueue) {
  784. XCTAssertTrue(session.requestTaskMap.isEmpty)
  785. XCTAssertTrue(session.activeRequests.isEmpty)
  786. }
  787. }
  788. func testThatSessionCallsRequestRetrierWhenUploadInitiallyEncountersAdaptError() {
  789. // Given
  790. let handler = UploadHandler()
  791. let session = Session(interceptor: handler)
  792. let expectation = self.expectation(description: "request should eventually fail")
  793. var response: DataResponse<Any>?
  794. let uploadData = Data("upload data".utf8)
  795. // When
  796. session.upload(uploadData, to: "https://httpbin.org/post")
  797. .validate()
  798. .responseJSON { jsonResponse in
  799. response = jsonResponse
  800. expectation.fulfill()
  801. }
  802. waitForExpectations(timeout: timeout, handler: nil)
  803. // Then
  804. XCTAssertEqual(handler.adaptCalledCount, 2)
  805. XCTAssertEqual(handler.adaptedCount, 2)
  806. XCTAssertEqual(handler.retryCalledCount, 1)
  807. XCTAssertEqual(handler.retryCount, 1)
  808. XCTAssertEqual(response?.result.isSuccess, true)
  809. assert(on: session.rootQueue) {
  810. XCTAssertTrue(session.requestTaskMap.isEmpty)
  811. XCTAssertTrue(session.activeRequests.isEmpty)
  812. }
  813. }
  814. func testThatSessionCallsAdapterWhenRequestIsRetried() {
  815. // Given
  816. let handler = RequestHandler()
  817. handler.shouldApplyAuthorizationHeader = true
  818. let session = Session(interceptor: handler)
  819. let expectation = self.expectation(description: "request should eventually succeed")
  820. var response: DataResponse<Any>?
  821. // When
  822. let request = session.request("https://httpbin.org/basic-auth/user/password")
  823. .validate()
  824. .responseJSON { jsonResponse in
  825. response = jsonResponse
  826. expectation.fulfill()
  827. }
  828. waitForExpectations(timeout: timeout, handler: nil)
  829. // Then
  830. XCTAssertEqual(handler.adaptCalledCount, 2)
  831. XCTAssertEqual(handler.adaptedCount, 2)
  832. XCTAssertEqual(handler.retryCalledCount, 1)
  833. XCTAssertEqual(handler.retryCount, 1)
  834. XCTAssertEqual(request.retryCount, 1)
  835. XCTAssertEqual(response?.result.isSuccess, true)
  836. assert(on: session.rootQueue) {
  837. XCTAssertTrue(session.requestTaskMap.isEmpty)
  838. XCTAssertTrue(session.activeRequests.isEmpty)
  839. }
  840. }
  841. func testThatSessionReturnsRequestAdaptationErrorWhenRequestIsRetried() {
  842. // Given
  843. let handler = RequestHandler()
  844. handler.throwsErrorOnSecondAdapt = true
  845. let session = Session(interceptor: handler)
  846. let expectation = self.expectation(description: "request should eventually fail")
  847. var response: DataResponse<Any>?
  848. // When
  849. let request = session.request("https://httpbin.org/basic-auth/user/password")
  850. .validate()
  851. .responseJSON { jsonResponse in
  852. response = jsonResponse
  853. expectation.fulfill()
  854. }
  855. waitForExpectations(timeout: timeout, handler: nil)
  856. // Then
  857. XCTAssertEqual(handler.adaptCalledCount, 2)
  858. XCTAssertEqual(handler.adaptedCount, 1)
  859. XCTAssertEqual(handler.retryCalledCount, 3)
  860. XCTAssertEqual(handler.retryCount, 3)
  861. XCTAssertEqual(request.retryCount, 1)
  862. XCTAssertEqual(response?.result.isSuccess, false)
  863. assert(on: session.rootQueue) {
  864. XCTAssertTrue(session.requestTaskMap.isEmpty)
  865. XCTAssertTrue(session.activeRequests.isEmpty)
  866. }
  867. if let error = response?.result.error?.asAFError {
  868. XCTAssertTrue(error.isRequestAdaptationError)
  869. XCTAssertEqual(error.underlyingError?.asAFError?.urlConvertible as? String, "/adapt/error/2")
  870. } else {
  871. XCTFail("error should not be nil")
  872. }
  873. }
  874. func testThatSessionRetriesRequestWithDelayWhenRetryResultContainsDelay() {
  875. // Given
  876. let handler = RequestHandler()
  877. handler.retryDelay = 0.01
  878. handler.throwsErrorOnSecondAdapt = true
  879. let session = Session(interceptor: handler)
  880. let expectation = self.expectation(description: "request should eventually fail")
  881. var response: DataResponse<Any>?
  882. // When
  883. let request = session.request("https://httpbin.org/basic-auth/user/password")
  884. .validate()
  885. .responseJSON { jsonResponse in
  886. response = jsonResponse
  887. expectation.fulfill()
  888. }
  889. waitForExpectations(timeout: timeout, handler: nil)
  890. // Then
  891. XCTAssertEqual(handler.adaptCalledCount, 2)
  892. XCTAssertEqual(handler.adaptedCount, 1)
  893. XCTAssertEqual(handler.retryCalledCount, 3)
  894. XCTAssertEqual(handler.retryCount, 3)
  895. XCTAssertEqual(request.retryCount, 1)
  896. XCTAssertEqual(response?.result.isSuccess, false)
  897. assert(on: session.rootQueue) {
  898. XCTAssertTrue(session.requestTaskMap.isEmpty)
  899. XCTAssertTrue(session.activeRequests.isEmpty)
  900. }
  901. if let error = response?.result.error?.asAFError {
  902. XCTAssertTrue(error.isRequestAdaptationError)
  903. XCTAssertEqual(error.underlyingError?.asAFError?.urlConvertible as? String, "/adapt/error/2")
  904. } else {
  905. XCTFail("error should not be nil")
  906. }
  907. }
  908. func testThatSessionReturnsRequestRetryErrorWhenRequestRetrierThrowsError() {
  909. // Given
  910. let handler = RequestHandler()
  911. handler.throwsErrorOnRetry = true
  912. let session = Session(interceptor: handler)
  913. let expectation = self.expectation(description: "request should eventually fail")
  914. var response: DataResponse<Any>?
  915. // When
  916. let request = session.request("https://httpbin.org/basic-auth/user/password")
  917. .validate()
  918. .responseJSON { jsonResponse in
  919. response = jsonResponse
  920. expectation.fulfill()
  921. }
  922. waitForExpectations(timeout: timeout, handler: nil)
  923. // Then
  924. XCTAssertEqual(handler.adaptCalledCount, 1)
  925. XCTAssertEqual(handler.adaptedCount, 1)
  926. XCTAssertEqual(handler.retryCalledCount, 2)
  927. XCTAssertEqual(handler.retryCount, 0)
  928. XCTAssertEqual(request.retryCount, 0)
  929. XCTAssertEqual(response?.result.isSuccess, false)
  930. assert(on: session.rootQueue) {
  931. XCTAssertTrue(session.requestTaskMap.isEmpty)
  932. XCTAssertTrue(session.activeRequests.isEmpty)
  933. }
  934. if let error = response?.result.error?.asAFError {
  935. XCTAssertTrue(error.isRequestRetryError)
  936. XCTAssertEqual(error.underlyingError?.asAFError?.urlConvertible as? String, "/invalid/url/2")
  937. } else {
  938. XCTFail("error should not be nil")
  939. }
  940. }
  941. // MARK: Tests - Response Serializer Retry
  942. func testThatSessionCallsRequestRetrierWhenResponseSerializerThrowsError() {
  943. // Given
  944. let handler = RequestHandler()
  945. handler.shouldRetry = false
  946. let session = Session()
  947. let expectation = self.expectation(description: "request should eventually fail")
  948. var response: DataResponse<Any>?
  949. // When
  950. let request = session.request("https://httpbin.org/image/jpeg", interceptor: handler)
  951. .validate()
  952. .responseJSON { jsonResponse in
  953. response = jsonResponse
  954. expectation.fulfill()
  955. }
  956. waitForExpectations(timeout: timeout, handler: nil)
  957. // Then
  958. XCTAssertEqual(handler.adaptCalledCount, 1)
  959. XCTAssertEqual(handler.adaptedCount, 1)
  960. XCTAssertEqual(handler.retryCalledCount, 1)
  961. XCTAssertEqual(handler.retryCount, 0)
  962. XCTAssertEqual(request.retryCount, 0)
  963. XCTAssertEqual(response?.result.isSuccess, false)
  964. assert(on: session.rootQueue) {
  965. XCTAssertTrue(session.requestTaskMap.isEmpty)
  966. XCTAssertTrue(session.activeRequests.isEmpty)
  967. }
  968. if let error = response?.error?.asAFError {
  969. XCTAssertTrue(error.isResponseSerializationError)
  970. XCTAssertTrue(error.localizedDescription.starts(with: "JSON could not be serialized"))
  971. } else {
  972. XCTFail("error should not be nil")
  973. }
  974. }
  975. func testThatSessionCallsRequestRetrierForAllResponseSerializersThatThrowError() throws {
  976. // Given
  977. let handler = RequestHandler()
  978. handler.throwsErrorOnRetry = true
  979. let session = Session()
  980. let json1Expectation = self.expectation(description: "request should eventually fail")
  981. var json1Response: DataResponse<Any>?
  982. let json2Expectation = self.expectation(description: "request should eventually fail")
  983. var json2Response: DataResponse<Any>?
  984. // When
  985. let request = session.request("https://httpbin.org/image/jpeg", interceptor: handler)
  986. .validate()
  987. .responseJSON { response in
  988. json1Response = response
  989. json1Expectation.fulfill()
  990. }
  991. .responseJSON { response in
  992. json2Response = response
  993. json2Expectation.fulfill()
  994. }
  995. waitForExpectations(timeout: timeout, handler: nil)
  996. // Then
  997. XCTAssertEqual(handler.adaptCalledCount, 1)
  998. XCTAssertEqual(handler.adaptedCount, 1)
  999. XCTAssertEqual(handler.retryCalledCount, 2)
  1000. XCTAssertEqual(handler.retryCount, 0)
  1001. XCTAssertEqual(request.retryCount, 0)
  1002. XCTAssertEqual(json1Response?.result.isSuccess, false)
  1003. XCTAssertEqual(json2Response?.result.isSuccess, false)
  1004. assert(on: session.rootQueue) {
  1005. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1006. XCTAssertTrue(session.activeRequests.isEmpty)
  1007. }
  1008. let errors: [AFError] = [json1Response, json2Response].compactMap { $0?.error?.asAFError }
  1009. XCTAssertEqual(errors.count, 2)
  1010. for (index, error) in errors.enumerated() {
  1011. XCTAssertTrue(error.isRequestRetryError)
  1012. XCTAssertEqual(error.localizedDescription.starts(with: "Request retry failed with retry error"), true)
  1013. if case let .requestRetryFailed(retryError, originalError) = error {
  1014. XCTAssertEqual(try retryError.asAFError?.urlConvertible?.asURL().absoluteString, "/invalid/url/\(index + 1)")
  1015. XCTAssertTrue(originalError.localizedDescription.starts(with: "JSON could not be serialized"))
  1016. } else {
  1017. XCTFail("Error failure reason should be response serialization failure")
  1018. }
  1019. }
  1020. }
  1021. func testThatSessionRetriesRequestImmediatelyWhenResponseSerializerRequestsRetry() throws {
  1022. // Given
  1023. let handler = RequestHandler()
  1024. let session = Session()
  1025. let json1Expectation = self.expectation(description: "request should eventually fail")
  1026. var json1Response: DataResponse<Any>?
  1027. let json2Expectation = self.expectation(description: "request should eventually fail")
  1028. var json2Response: DataResponse<Any>?
  1029. // When
  1030. let request = session.request("https://httpbin.org/image/jpeg", interceptor: handler)
  1031. .validate()
  1032. .responseJSON { response in
  1033. json1Response = response
  1034. json1Expectation.fulfill()
  1035. }
  1036. .responseJSON { response in
  1037. json2Response = response
  1038. json2Expectation.fulfill()
  1039. }
  1040. waitForExpectations(timeout: 10, handler: nil)
  1041. // Then
  1042. XCTAssertEqual(handler.adaptCalledCount, 2)
  1043. XCTAssertEqual(handler.adaptedCount, 2)
  1044. XCTAssertEqual(handler.retryCalledCount, 3)
  1045. XCTAssertEqual(handler.retryCount, 3)
  1046. XCTAssertEqual(request.retryCount, 1)
  1047. XCTAssertEqual(json1Response?.result.isSuccess, false)
  1048. XCTAssertEqual(json2Response?.result.isSuccess, false)
  1049. assert(on: session.rootQueue) {
  1050. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1051. XCTAssertTrue(session.activeRequests.isEmpty)
  1052. }
  1053. let errors: [AFError] = [json1Response, json2Response].compactMap { $0?.error?.asAFError }
  1054. XCTAssertEqual(errors.count, 2)
  1055. for error in errors {
  1056. XCTAssertTrue(error.isResponseSerializationError)
  1057. XCTAssertTrue(error.localizedDescription.starts(with: "JSON could not be serialized"))
  1058. }
  1059. }
  1060. func testThatSessionCallsResponseSerializerCompletionsWhenAdapterThrowsErrorDuringRetry() {
  1061. // Four retries should occur given this scenario:
  1062. // 1) Retrier is called from first response serializer failure (trips retry)
  1063. // 2) Retrier is called by Session for adapt error thrown
  1064. // 3) Retrier is called again from first response serializer failure
  1065. // 4) Retrier is called from second response serializer failure
  1066. // Given
  1067. let handler = RequestHandler()
  1068. handler.throwsErrorOnSecondAdapt = true
  1069. let session = Session()
  1070. let json1Expectation = self.expectation(description: "request should eventually fail")
  1071. var json1Response: DataResponse<Any>?
  1072. let json2Expectation = self.expectation(description: "request should eventually fail")
  1073. var json2Response: DataResponse<Any>?
  1074. // When
  1075. let request = session.request("https://httpbin.org/image/jpeg", interceptor: handler)
  1076. .validate()
  1077. .responseJSON { response in
  1078. json1Response = response
  1079. json1Expectation.fulfill()
  1080. }
  1081. .responseJSON { response in
  1082. json2Response = response
  1083. json2Expectation.fulfill()
  1084. }
  1085. waitForExpectations(timeout: 10, handler: nil)
  1086. // Then
  1087. XCTAssertEqual(handler.adaptCalledCount, 2)
  1088. XCTAssertEqual(handler.adaptedCount, 1)
  1089. XCTAssertEqual(handler.retryCalledCount, 4)
  1090. XCTAssertEqual(handler.retryCount, 4)
  1091. XCTAssertEqual(request.retryCount, 1)
  1092. XCTAssertEqual(json1Response?.result.isSuccess, false)
  1093. XCTAssertEqual(json2Response?.result.isSuccess, false)
  1094. assert(on: session.rootQueue) {
  1095. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1096. XCTAssertTrue(session.activeRequests.isEmpty)
  1097. }
  1098. let errors: [AFError] = [json1Response, json2Response].compactMap { $0?.error?.asAFError }
  1099. XCTAssertEqual(errors.count, 2)
  1100. for error in errors {
  1101. XCTAssertTrue(error.isRequestAdaptationError)
  1102. XCTAssertEqual(error.localizedDescription, "Request adaption failed with error: URL is not valid: /adapt/error/2")
  1103. }
  1104. }
  1105. func testThatSessionCallsResponseSerializerCompletionsWhenAdapterThrowsErrorDuringRetryForDownloads() {
  1106. // Four retries should occur given this scenario:
  1107. // 1) Retrier is called from first response serializer failure (trips retry)
  1108. // 2) Retrier is called by Session for adapt error thrown
  1109. // 3) Retrier is called again from first response serializer failure
  1110. // 4) Retrier is called from second response serializer failure
  1111. // Given
  1112. let handler = RequestHandler()
  1113. handler.throwsErrorOnSecondAdapt = true
  1114. let session = Session()
  1115. let json1Expectation = self.expectation(description: "request should eventually fail")
  1116. var json1Response: DownloadResponse<Any>?
  1117. let json2Expectation = self.expectation(description: "request should eventually fail")
  1118. var json2Response: DownloadResponse<Any>?
  1119. // When
  1120. let request = session.download("https://httpbin.org/image/jpeg", interceptor: handler)
  1121. .validate()
  1122. .responseJSON { response in
  1123. json1Response = response
  1124. json1Expectation.fulfill()
  1125. }
  1126. .responseJSON { response in
  1127. json2Response = response
  1128. json2Expectation.fulfill()
  1129. }
  1130. waitForExpectations(timeout: 10, handler: nil)
  1131. // Then
  1132. XCTAssertEqual(handler.adaptCalledCount, 2)
  1133. XCTAssertEqual(handler.adaptedCount, 1)
  1134. XCTAssertEqual(handler.retryCalledCount, 4)
  1135. XCTAssertEqual(handler.retryCount, 4)
  1136. XCTAssertEqual(request.retryCount, 1)
  1137. XCTAssertEqual(json1Response?.result.isSuccess, false)
  1138. XCTAssertEqual(json2Response?.result.isSuccess, false)
  1139. assert(on: session.rootQueue) {
  1140. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1141. XCTAssertTrue(session.activeRequests.isEmpty)
  1142. }
  1143. let errors: [AFError] = [json1Response, json2Response].compactMap { $0?.error?.asAFError }
  1144. XCTAssertEqual(errors.count, 2)
  1145. for error in errors {
  1146. XCTAssertTrue(error.isRequestAdaptationError)
  1147. XCTAssertEqual(error.localizedDescription, "Request adaption failed with error: URL is not valid: /adapt/error/2")
  1148. }
  1149. }
  1150. // MARK: Tests - Session Invalidation
  1151. func testThatSessionIsInvalidatedAndAllRequestsCompleteWhenSessionIsDeinitialized() {
  1152. // Given
  1153. let invalidationExpectation = expectation(description: "sessionDidBecomeInvalidWithError should be called")
  1154. let events = ClosureEventMonitor()
  1155. events.sessionDidBecomeInvalidWithError = { (_, _) in
  1156. invalidationExpectation.fulfill()
  1157. }
  1158. var session: Session? = Session(startRequestsImmediately: false, eventMonitors: [events])
  1159. var error: Error?
  1160. let requestExpectation = expectation(description: "request should complete")
  1161. // When
  1162. session?.request(URLRequest.makeHTTPBinRequest()).response { (response) in
  1163. error = response.error
  1164. requestExpectation.fulfill()
  1165. }
  1166. session = nil
  1167. waitForExpectations(timeout: timeout, handler: nil)
  1168. // Then
  1169. assertErrorIsAFError(error) { XCTAssertTrue($0.isSessionDeinitializedError) }
  1170. }
  1171. // MARK: Tests - Request Cancellation
  1172. func testThatSessionOnlyCallsResponseSerializerCompletionWhenCancellingInsideCompletion() {
  1173. // Given
  1174. let handler = RequestHandler()
  1175. let session = Session()
  1176. let expectation = self.expectation(description: "request should complete")
  1177. var response: DataResponse<Any>?
  1178. var completionCallCount = 0
  1179. // When
  1180. let request = session.request("https://httpbin.org/get", interceptor: handler)
  1181. request.validate()
  1182. request.responseJSON { resp in
  1183. request.cancel()
  1184. response = resp
  1185. completionCallCount += 1
  1186. DispatchQueue.main.after(0.01) { expectation.fulfill() }
  1187. }
  1188. waitForExpectations(timeout: timeout, handler: nil)
  1189. // Then
  1190. XCTAssertEqual(handler.adaptCalledCount, 1)
  1191. XCTAssertEqual(handler.adaptedCount, 1)
  1192. XCTAssertEqual(handler.retryCalledCount, 0)
  1193. XCTAssertEqual(handler.retryCount, 0)
  1194. XCTAssertEqual(request.retryCount, 0)
  1195. XCTAssertEqual(response?.result.isSuccess, true)
  1196. XCTAssertEqual(completionCallCount, 1)
  1197. assert(on: session.rootQueue) {
  1198. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1199. XCTAssertTrue(session.activeRequests.isEmpty)
  1200. }
  1201. }
  1202. // MARK: Tests - Request State
  1203. func testThatSessionSetsRequestStateWhenStartRequestsImmediatelyIsTrue() {
  1204. // Given
  1205. let session = Session()
  1206. let expectation = self.expectation(description: "request should complete")
  1207. var response: DataResponse<Any>?
  1208. // When
  1209. let request = session.request("https://httpbin.org/get").responseJSON { resp in
  1210. response = resp
  1211. expectation.fulfill()
  1212. }
  1213. waitForExpectations(timeout: timeout, handler: nil)
  1214. // Then
  1215. XCTAssertEqual(request.state, .finished)
  1216. XCTAssertEqual(response?.result.isSuccess, true)
  1217. }
  1218. }
  1219. // MARK: -
  1220. final class SessionCancellationTestCase: BaseTestCase {
  1221. func testThatAutomaticallyResumedRequestsCanBeMassCancelled() {
  1222. // Given
  1223. let count = 100
  1224. let session = Session()
  1225. var responses: [DataResponse<Data?>] = []
  1226. let completion = expectation(description: "all requests should finish")
  1227. completion.expectedFulfillmentCount = count
  1228. let cancellation = expectation(description: "cancel all requests should be called")
  1229. // When
  1230. for _ in 1...count {
  1231. let request = URLRequest.makeHTTPBinRequest(path: "delay/1")
  1232. session.request(request).response { (response) in
  1233. responses.append(response)
  1234. completion.fulfill()
  1235. }
  1236. }
  1237. session.cancelAllRequests {
  1238. cancellation.fulfill()
  1239. }
  1240. waitForExpectations(timeout: timeout)
  1241. // Then
  1242. XCTAssertTrue(responses.allSatisfy { $0.error?.asAFError?.isExplicitlyCancelledError == true })
  1243. assert(on: session.rootQueue) {
  1244. XCTAssertTrue(session.requestTaskMap.isEmpty, "requestTaskMap should be empty but has \(session.requestTaskMap.count) items")
  1245. XCTAssertTrue(session.activeRequests.isEmpty, "activeRequests should be empty but has \(session.activeRequests.count) items")
  1246. }
  1247. }
  1248. func testThatManuallyResumedRequestsCanBeMassCancelled() {
  1249. // Given
  1250. let count = 100
  1251. let session = Session(startRequestsImmediately: false)
  1252. let request = URLRequest.makeHTTPBinRequest(path: "delay/1")
  1253. var responses: [DataResponse<Data?>] = []
  1254. let completion = expectation(description: "all requests should finish")
  1255. completion.expectedFulfillmentCount = count
  1256. let cancellation = expectation(description: "cancel all requests should be called")
  1257. // When
  1258. for _ in 1...count {
  1259. session.request(request).response { (response) in
  1260. responses.append(response)
  1261. completion.fulfill()
  1262. }
  1263. }
  1264. session.cancelAllRequests {
  1265. cancellation.fulfill()
  1266. }
  1267. waitForExpectations(timeout: timeout)
  1268. // Then
  1269. XCTAssertTrue(responses.allSatisfy { $0.error?.asAFError?.isExplicitlyCancelledError == true })
  1270. assert(on: session.rootQueue) {
  1271. XCTAssertTrue(session.requestTaskMap.isEmpty, "requestTaskMap should be empty but has \(session.requestTaskMap.count) items")
  1272. XCTAssertTrue(session.activeRequests.isEmpty, "activeRequests should be empty but has \(session.activeRequests.count) items")
  1273. }
  1274. }
  1275. func testThatRetriedRequestsCanBeMassCancelled() {
  1276. // Given
  1277. final class OnceRetrier: RequestInterceptor {
  1278. private var hasRetried = false
  1279. func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
  1280. completion(hasRetried ? .doNotRetry : .retry)
  1281. hasRetried = true
  1282. }
  1283. }
  1284. let monitor = ClosureEventMonitor()
  1285. let session = Session(interceptor: OnceRetrier(), eventMonitors: [monitor])
  1286. let request = URLRequest.makeHTTPBinRequest(path: "status/401")
  1287. let completion = expectation(description: "all requests should finish")
  1288. let cancellation = expectation(description: "cancel all requests should be called")
  1289. let createTask = expectation(description: "should create task twice")
  1290. createTask.expectedFulfillmentCount = 2
  1291. monitor.requestDidCreateTask = { (_, _) in
  1292. createTask.fulfill()
  1293. }
  1294. // Cancel when retry starts.
  1295. monitor.requestIsRetrying = { _ in
  1296. session.cancelAllRequests {
  1297. cancellation.fulfill()
  1298. }
  1299. }
  1300. var received: DataResponse<Data?>?
  1301. // When
  1302. session.request(request).validate().response { (response) in
  1303. received = response
  1304. completion.fulfill()
  1305. }
  1306. waitForExpectations(timeout: timeout)
  1307. // Then
  1308. XCTAssertTrue(received?.error?.asAFError?.isExplicitlyCancelledError == true)
  1309. assert(on: session.rootQueue) {
  1310. XCTAssertTrue(session.requestTaskMap.isEmpty, "requestTaskMap should be empty but has \(session.requestTaskMap.count) items")
  1311. XCTAssertTrue(session.activeRequests.isEmpty, "activeRequests should be empty but has \(session.activeRequests.count) items")
  1312. }
  1313. }
  1314. }
  1315. // MARK: -
  1316. final class SessionConfigurationHeadersTestCase: BaseTestCase {
  1317. enum ConfigurationType {
  1318. case `default`, ephemeral, background
  1319. }
  1320. func testThatDefaultConfigurationHeadersAreSentWithRequest() {
  1321. // Given, When, Then
  1322. executeAuthorizationHeaderTest(for: .default)
  1323. }
  1324. func testThatEphemeralConfigurationHeadersAreSentWithRequest() {
  1325. // Given, When, Then
  1326. executeAuthorizationHeaderTest(for: .ephemeral)
  1327. }
  1328. #if os(macOS)
  1329. func testThatBackgroundConfigurationHeadersAreSentWithRequest() {
  1330. // Given, When, Then
  1331. executeAuthorizationHeaderTest(for: .background)
  1332. }
  1333. #endif
  1334. private func executeAuthorizationHeaderTest(for type: ConfigurationType) {
  1335. // Given
  1336. let session: Session = {
  1337. let configuration: URLSessionConfiguration = {
  1338. let configuration: URLSessionConfiguration
  1339. switch type {
  1340. case .default:
  1341. configuration = .default
  1342. case .ephemeral:
  1343. configuration = .ephemeral
  1344. case .background:
  1345. let identifier = "org.alamofire.test.manager-configuration-tests"
  1346. configuration = .background(withIdentifier: identifier)
  1347. }
  1348. var headers = HTTPHeaders.default
  1349. headers["Authorization"] = "Bearer 123456"
  1350. configuration.headers = headers
  1351. return configuration
  1352. }()
  1353. return Session(configuration: configuration)
  1354. }()
  1355. let expectation = self.expectation(description: "request should complete successfully")
  1356. var response: DataResponse<Any>?
  1357. // When
  1358. session.request("https://httpbin.org/headers")
  1359. .responseJSON { closureResponse in
  1360. response = closureResponse
  1361. expectation.fulfill()
  1362. }
  1363. waitForExpectations(timeout: timeout, handler: nil)
  1364. // Then
  1365. if let response = response {
  1366. XCTAssertNotNil(response.request, "request should not be nil")
  1367. XCTAssertNotNil(response.response, "response should not be nil")
  1368. XCTAssertNotNil(response.data, "data should not be nil")
  1369. XCTAssertTrue(response.result.isSuccess, "result should be a success")
  1370. if
  1371. let response = response.result.value as? [String: Any],
  1372. let headers = response["headers"] as? [String: String],
  1373. let authorization = headers["Authorization"]
  1374. {
  1375. XCTAssertEqual(authorization, "Bearer 123456", "authorization header value does not match")
  1376. } else {
  1377. XCTFail("failed to extract authorization header value")
  1378. }
  1379. } else {
  1380. XCTFail("response should not be nil")
  1381. }
  1382. }
  1383. }