SessionTests.swift 65 KB

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