ValidationTests.swift 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. //
  2. // ValidationTests.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 StatusCodeValidationTestCase: BaseTestCase {
  28. func testThatValidationForRequestWithAcceptableStatusCodeResponseSucceeds() {
  29. // Given
  30. let urlString = "https://httpbin.org/status/200"
  31. let expectation1 = expectation(description: "request should return 200 status code")
  32. let expectation2 = expectation(description: "download should return 200 status code")
  33. var requestError: AFError?
  34. var downloadError: AFError?
  35. // When
  36. AF.request(urlString)
  37. .validate(statusCode: 200..<300)
  38. .response { resp in
  39. requestError = resp.error
  40. expectation1.fulfill()
  41. }
  42. AF.download(urlString)
  43. .validate(statusCode: 200..<300)
  44. .response { resp in
  45. downloadError = resp.error
  46. expectation2.fulfill()
  47. }
  48. waitForExpectations(timeout: timeout, handler: nil)
  49. // Then
  50. XCTAssertNil(requestError)
  51. XCTAssertNil(downloadError)
  52. }
  53. func testThatValidationForRequestWithUnacceptableStatusCodeResponseFails() {
  54. // Given
  55. let urlString = "https://httpbin.org/status/404"
  56. let expectation1 = expectation(description: "request should return 404 status code")
  57. let expectation2 = expectation(description: "download should return 404 status code")
  58. var requestError: AFError?
  59. var downloadError: AFError?
  60. // When
  61. AF.request(urlString)
  62. .validate(statusCode: [200])
  63. .response { resp in
  64. requestError = resp.error
  65. expectation1.fulfill()
  66. }
  67. AF.download(urlString)
  68. .validate(statusCode: [200])
  69. .response { resp in
  70. downloadError = resp.error
  71. expectation2.fulfill()
  72. }
  73. waitForExpectations(timeout: timeout, handler: nil)
  74. // Then
  75. XCTAssertNotNil(requestError)
  76. XCTAssertNotNil(downloadError)
  77. for error in [requestError, downloadError] {
  78. XCTAssertEqual(error?.isUnacceptableStatusCode, true)
  79. XCTAssertEqual(error?.responseCode, 404)
  80. }
  81. }
  82. func testThatValidationForRequestWithNoAcceptableStatusCodesFails() {
  83. // Given
  84. let urlString = "https://httpbin.org/status/201"
  85. let expectation1 = expectation(description: "request should return 201 status code")
  86. let expectation2 = expectation(description: "download should return 201 status code")
  87. var requestError: AFError?
  88. var downloadError: AFError?
  89. // When
  90. AF.request(urlString)
  91. .validate(statusCode: [])
  92. .response { resp in
  93. requestError = resp.error
  94. expectation1.fulfill()
  95. }
  96. AF.download(urlString)
  97. .validate(statusCode: [])
  98. .response { resp in
  99. downloadError = resp.error
  100. expectation2.fulfill()
  101. }
  102. waitForExpectations(timeout: timeout, handler: nil)
  103. // Then
  104. XCTAssertNotNil(requestError)
  105. XCTAssertNotNil(downloadError)
  106. for error in [requestError, downloadError] {
  107. XCTAssertEqual(error?.isUnacceptableStatusCode, true)
  108. XCTAssertEqual(error?.responseCode, 201)
  109. }
  110. }
  111. }
  112. // MARK: -
  113. final class ContentTypeValidationTestCase: BaseTestCase {
  114. func testThatValidationForRequestWithAcceptableContentTypeResponseSucceeds() {
  115. // Given
  116. let urlString = "https://httpbin.org/ip"
  117. let expectation1 = expectation(description: "request should succeed and return ip")
  118. let expectation2 = expectation(description: "download should succeed and return ip")
  119. var requestError: AFError?
  120. var downloadError: AFError?
  121. // When
  122. AF.request(urlString)
  123. .validate(contentType: ["application/json"])
  124. .validate(contentType: ["application/json; charset=utf-8"])
  125. .validate(contentType: ["application/json; q=0.8; charset=utf-8"])
  126. .response { resp in
  127. requestError = resp.error
  128. expectation1.fulfill()
  129. }
  130. AF.download(urlString)
  131. .validate(contentType: ["application/json"])
  132. .validate(contentType: ["application/json; charset=utf-8"])
  133. .validate(contentType: ["application/json; q=0.8; charset=utf-8"])
  134. .response { resp in
  135. downloadError = resp.error
  136. expectation2.fulfill()
  137. }
  138. waitForExpectations(timeout: timeout, handler: nil)
  139. // Then
  140. XCTAssertNil(requestError)
  141. XCTAssertNil(downloadError)
  142. }
  143. func testThatValidationForRequestWithAcceptableWildcardContentTypeResponseSucceeds() {
  144. // Given
  145. let urlString = "https://httpbin.org/ip"
  146. let expectation1 = expectation(description: "request should succeed and return ip")
  147. let expectation2 = expectation(description: "download should succeed and return ip")
  148. var requestError: AFError?
  149. var downloadError: AFError?
  150. // When
  151. AF.request(urlString)
  152. .validate(contentType: ["*/*"])
  153. .validate(contentType: ["application/*"])
  154. .validate(contentType: ["*/json"])
  155. .response { resp in
  156. requestError = resp.error
  157. expectation1.fulfill()
  158. }
  159. AF.download(urlString)
  160. .validate(contentType: ["*/*"])
  161. .validate(contentType: ["application/*"])
  162. .validate(contentType: ["*/json"])
  163. .response { resp in
  164. downloadError = resp.error
  165. expectation2.fulfill()
  166. }
  167. waitForExpectations(timeout: timeout, handler: nil)
  168. // Then
  169. XCTAssertNil(requestError)
  170. XCTAssertNil(downloadError)
  171. }
  172. func testThatValidationForRequestWithUnacceptableContentTypeResponseFails() {
  173. // Given
  174. let urlString = "https://httpbin.org/xml"
  175. let expectation1 = expectation(description: "request should succeed and return xml")
  176. let expectation2 = expectation(description: "download should succeed and return xml")
  177. var requestError: AFError?
  178. var downloadError: AFError?
  179. // When
  180. AF.request(urlString)
  181. .validate(contentType: ["application/octet-stream"])
  182. .response { resp in
  183. requestError = resp.error
  184. expectation1.fulfill()
  185. }
  186. AF.download(urlString)
  187. .validate(contentType: ["application/octet-stream"])
  188. .response { resp in
  189. downloadError = resp.error
  190. expectation2.fulfill()
  191. }
  192. waitForExpectations(timeout: timeout, handler: nil)
  193. // Then
  194. XCTAssertNotNil(requestError)
  195. XCTAssertNotNil(downloadError)
  196. for error in [requestError, downloadError] {
  197. XCTAssertEqual(error?.isUnacceptableContentType, true)
  198. XCTAssertEqual(error?.responseContentType, "application/xml")
  199. XCTAssertEqual(error?.acceptableContentTypes?.first, "application/octet-stream")
  200. }
  201. }
  202. func testThatValidationForRequestWithNoAcceptableContentTypeResponseFails() {
  203. // Given
  204. let urlString = "https://httpbin.org/xml"
  205. let expectation1 = expectation(description: "request should succeed and return xml")
  206. let expectation2 = expectation(description: "download should succeed and return xml")
  207. var requestError: AFError?
  208. var downloadError: AFError?
  209. // When
  210. AF.request(urlString)
  211. .validate(contentType: [])
  212. .response { resp in
  213. requestError = resp.error
  214. expectation1.fulfill()
  215. }
  216. AF.download(urlString)
  217. .validate(contentType: [])
  218. .response { resp in
  219. downloadError = resp.error
  220. expectation2.fulfill()
  221. }
  222. waitForExpectations(timeout: timeout, handler: nil)
  223. // Then
  224. XCTAssertNotNil(requestError)
  225. XCTAssertNotNil(downloadError)
  226. for error in [requestError, downloadError] {
  227. XCTAssertEqual(error?.isUnacceptableContentType, true)
  228. XCTAssertEqual(error?.responseContentType, "application/xml")
  229. XCTAssertEqual(error?.acceptableContentTypes?.isEmpty, true)
  230. }
  231. }
  232. func testThatValidationForRequestWithNoAcceptableContentTypeResponseSucceedsWhenNoDataIsReturned() {
  233. // Given
  234. let urlString = "https://httpbin.org/status/204"
  235. let expectation1 = expectation(description: "request should succeed and return no data")
  236. let expectation2 = expectation(description: "download should succeed and return no data")
  237. var requestError: AFError?
  238. var downloadError: AFError?
  239. // When
  240. AF.request(urlString)
  241. .validate(contentType: [])
  242. .response { resp in
  243. requestError = resp.error
  244. expectation1.fulfill()
  245. }
  246. AF.download(urlString)
  247. .validate(contentType: [])
  248. .response { resp in
  249. downloadError = resp.error
  250. expectation2.fulfill()
  251. }
  252. waitForExpectations(timeout: timeout, handler: nil)
  253. // Then
  254. XCTAssertNil(requestError)
  255. XCTAssertNil(downloadError)
  256. }
  257. func testThatValidationForRequestWithAcceptableWildcardContentTypeResponseSucceedsWhenResponseIsNil() {
  258. // Given
  259. class MockManager: Session {
  260. override func request(_ convertible: URLRequestConvertible,
  261. interceptor: RequestInterceptor? = nil) -> DataRequest {
  262. let request = MockDataRequest(convertible: convertible,
  263. underlyingQueue: rootQueue,
  264. serializationQueue: serializationQueue,
  265. eventMonitor: eventMonitor,
  266. interceptor: interceptor,
  267. delegate: self)
  268. perform(request)
  269. return request
  270. }
  271. override func download(_ convertible: URLRequestConvertible,
  272. interceptor: RequestInterceptor? = nil,
  273. to destination: DownloadRequest.Destination?)
  274. -> DownloadRequest {
  275. let request = MockDownloadRequest(downloadable: .request(convertible),
  276. underlyingQueue: rootQueue,
  277. serializationQueue: serializationQueue,
  278. eventMonitor: eventMonitor,
  279. interceptor: interceptor,
  280. delegate: self,
  281. destination: destination ?? MockDownloadRequest.defaultDestination)
  282. perform(request)
  283. return request
  284. }
  285. }
  286. class MockDataRequest: DataRequest {
  287. override var response: HTTPURLResponse? {
  288. MockHTTPURLResponse(url: request!.url!,
  289. statusCode: 204,
  290. httpVersion: "HTTP/1.1",
  291. headerFields: nil)
  292. }
  293. }
  294. class MockDownloadRequest: DownloadRequest {
  295. override var response: HTTPURLResponse? {
  296. MockHTTPURLResponse(url: request!.url!,
  297. statusCode: 204,
  298. httpVersion: "HTTP/1.1",
  299. headerFields: nil)
  300. }
  301. }
  302. class MockHTTPURLResponse: HTTPURLResponse {
  303. override var mimeType: String? { nil }
  304. }
  305. let manager: Session = {
  306. let configuration: URLSessionConfiguration = {
  307. let configuration = URLSessionConfiguration.ephemeral
  308. configuration.headers = HTTPHeaders.default
  309. return configuration
  310. }()
  311. return MockManager(configuration: configuration)
  312. }()
  313. let urlString = "https://httpbin.org/delete"
  314. let expectation1 = expectation(description: "request should be stubbed and return 204 status code")
  315. let expectation2 = expectation(description: "download should be stubbed and return 204 status code")
  316. var requestResponse: DataResponse<Data?, AFError>?
  317. var downloadResponse: DownloadResponse<URL?, AFError>?
  318. // When
  319. manager.request(urlString, method: .delete)
  320. .validate(contentType: ["*/*"])
  321. .response { resp in
  322. requestResponse = resp
  323. expectation1.fulfill()
  324. }
  325. manager.download(urlString, method: .delete)
  326. .validate(contentType: ["*/*"])
  327. .response { resp in
  328. downloadResponse = resp
  329. expectation2.fulfill()
  330. }
  331. waitForExpectations(timeout: timeout, handler: nil)
  332. // Then
  333. XCTAssertNotNil(requestResponse?.response)
  334. XCTAssertNotNil(requestResponse?.data)
  335. XCTAssertNil(requestResponse?.error)
  336. XCTAssertEqual(requestResponse?.response?.statusCode, 204)
  337. XCTAssertNil(requestResponse?.response?.mimeType)
  338. XCTAssertNotNil(downloadResponse?.response)
  339. XCTAssertNotNil(downloadResponse?.fileURL)
  340. XCTAssertNil(downloadResponse?.error)
  341. XCTAssertEqual(downloadResponse?.response?.statusCode, 204)
  342. XCTAssertNil(downloadResponse?.response?.mimeType)
  343. }
  344. }
  345. // MARK: -
  346. final class MultipleValidationTestCase: BaseTestCase {
  347. func testThatValidationForRequestWithAcceptableStatusCodeAndContentTypeResponseSucceeds() {
  348. // Given
  349. let urlString = "https://httpbin.org/ip"
  350. let expectation1 = expectation(description: "request should succeed and return ip")
  351. let expectation2 = expectation(description: "request should succeed and return ip")
  352. var requestError: AFError?
  353. var downloadError: AFError?
  354. // When
  355. AF.request(urlString)
  356. .validate(statusCode: 200..<300)
  357. .validate(contentType: ["application/json"])
  358. .response { resp in
  359. requestError = resp.error
  360. expectation1.fulfill()
  361. }
  362. AF.download(urlString)
  363. .validate(statusCode: 200..<300)
  364. .validate(contentType: ["application/json"])
  365. .response { resp in
  366. downloadError = resp.error
  367. expectation2.fulfill()
  368. }
  369. waitForExpectations(timeout: timeout, handler: nil)
  370. // Then
  371. XCTAssertNil(requestError)
  372. XCTAssertNil(downloadError)
  373. }
  374. func testThatValidationForRequestWithUnacceptableStatusCodeAndContentTypeResponseFailsWithStatusCodeError() {
  375. // Given
  376. let urlString = "https://httpbin.org/xml"
  377. let expectation1 = expectation(description: "request should succeed and return xml")
  378. let expectation2 = expectation(description: "download should succeed and return xml")
  379. var requestError: AFError?
  380. var downloadError: AFError?
  381. // When
  382. AF.request(urlString)
  383. .validate(statusCode: 400..<600)
  384. .validate(contentType: ["application/octet-stream"])
  385. .response { resp in
  386. requestError = resp.error
  387. expectation1.fulfill()
  388. }
  389. AF.download(urlString)
  390. .validate(statusCode: 400..<600)
  391. .validate(contentType: ["application/octet-stream"])
  392. .response { resp in
  393. downloadError = resp.error
  394. expectation2.fulfill()
  395. }
  396. waitForExpectations(timeout: timeout, handler: nil)
  397. // Then
  398. XCTAssertNotNil(requestError)
  399. XCTAssertNotNil(downloadError)
  400. for error in [requestError, downloadError] {
  401. XCTAssertEqual(error?.isUnacceptableStatusCode, true)
  402. XCTAssertEqual(error?.responseCode, 200)
  403. }
  404. }
  405. func testThatValidationForRequestWithUnacceptableStatusCodeAndContentTypeResponseFailsWithContentTypeError() {
  406. // Given
  407. let urlString = "https://httpbin.org/xml"
  408. let expectation1 = expectation(description: "request should succeed and return xml")
  409. let expectation2 = expectation(description: "download should succeed and return xml")
  410. var requestError: AFError?
  411. var downloadError: AFError?
  412. // When
  413. AF.request(urlString)
  414. .validate(contentType: ["application/octet-stream"])
  415. .validate(statusCode: 400..<600)
  416. .response { resp in
  417. requestError = resp.error
  418. expectation1.fulfill()
  419. }
  420. AF.download(urlString)
  421. .validate(contentType: ["application/octet-stream"])
  422. .validate(statusCode: 400..<600)
  423. .response { resp in
  424. downloadError = resp.error
  425. expectation2.fulfill()
  426. }
  427. waitForExpectations(timeout: timeout, handler: nil)
  428. // Then
  429. XCTAssertNotNil(requestError)
  430. XCTAssertNotNil(downloadError)
  431. for error in [requestError, downloadError] {
  432. XCTAssertEqual(error?.isUnacceptableContentType, true)
  433. XCTAssertEqual(error?.responseContentType, "application/xml")
  434. XCTAssertEqual(error?.acceptableContentTypes?.first, "application/octet-stream")
  435. }
  436. }
  437. }
  438. // MARK: -
  439. final class AutomaticValidationTestCase: BaseTestCase {
  440. func testThatValidationForRequestWithAcceptableStatusCodeAndContentTypeResponseSucceeds() {
  441. // Given
  442. let url = URL(string: "https://httpbin.org/ip")!
  443. var urlRequest = URLRequest(url: url)
  444. urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
  445. let expectation1 = expectation(description: "request should succeed and return ip")
  446. let expectation2 = expectation(description: "download should succeed and return ip")
  447. var requestError: AFError?
  448. var downloadError: AFError?
  449. // When
  450. AF.request(urlRequest).validate().response { resp in
  451. requestError = resp.error
  452. expectation1.fulfill()
  453. }
  454. AF.download(urlRequest).validate().response { resp in
  455. downloadError = resp.error
  456. expectation2.fulfill()
  457. }
  458. waitForExpectations(timeout: timeout, handler: nil)
  459. // Then
  460. XCTAssertNil(requestError)
  461. XCTAssertNil(downloadError)
  462. }
  463. func testThatValidationForRequestWithUnacceptableStatusCodeResponseFails() {
  464. // Given
  465. let urlString = "https://httpbin.org/status/404"
  466. let expectation1 = expectation(description: "request should return 404 status code")
  467. let expectation2 = expectation(description: "download should return 404 status code")
  468. var requestError: AFError?
  469. var downloadError: AFError?
  470. // When
  471. AF.request(urlString)
  472. .validate()
  473. .response { resp in
  474. requestError = resp.error
  475. expectation1.fulfill()
  476. }
  477. AF.download(urlString)
  478. .validate()
  479. .response { resp in
  480. downloadError = resp.error
  481. expectation2.fulfill()
  482. }
  483. waitForExpectations(timeout: timeout, handler: nil)
  484. // Then
  485. XCTAssertNotNil(requestError)
  486. XCTAssertNotNil(downloadError)
  487. for error in [requestError, downloadError] {
  488. XCTAssertEqual(error?.isUnacceptableStatusCode, true)
  489. XCTAssertEqual(error?.responseCode, 404)
  490. }
  491. }
  492. func testThatValidationForRequestWithAcceptableWildcardContentTypeResponseSucceeds() {
  493. // Given
  494. let url = URL(string: "https://httpbin.org/ip")!
  495. var urlRequest = URLRequest(url: url)
  496. urlRequest.setValue("application/*", forHTTPHeaderField: "Accept")
  497. let expectation1 = expectation(description: "request should succeed and return ip")
  498. let expectation2 = expectation(description: "download should succeed and return ip")
  499. var requestError: AFError?
  500. var downloadError: AFError?
  501. // When
  502. AF.request(urlRequest).validate().response { resp in
  503. requestError = resp.error
  504. expectation1.fulfill()
  505. }
  506. AF.download(urlRequest).validate().response { resp in
  507. downloadError = resp.error
  508. expectation2.fulfill()
  509. }
  510. waitForExpectations(timeout: timeout, handler: nil)
  511. // Then
  512. XCTAssertNil(requestError)
  513. XCTAssertNil(downloadError)
  514. }
  515. func testThatValidationForRequestWithAcceptableComplexContentTypeResponseSucceeds() {
  516. // Given
  517. let url = URL(string: "https://httpbin.org/xml")!
  518. var urlRequest = URLRequest(url: url)
  519. let headerValue = "text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8,*/*;q=0.5"
  520. urlRequest.setValue(headerValue, forHTTPHeaderField: "Accept")
  521. let expectation1 = expectation(description: "request should succeed and return xml")
  522. let expectation2 = expectation(description: "request should succeed and return xml")
  523. var requestError: AFError?
  524. var downloadError: AFError?
  525. // When
  526. AF.request(urlRequest).validate().response { resp in
  527. requestError = resp.error
  528. expectation1.fulfill()
  529. }
  530. AF.download(urlRequest).validate().response { resp in
  531. downloadError = resp.error
  532. expectation2.fulfill()
  533. }
  534. waitForExpectations(timeout: timeout, handler: nil)
  535. // Then
  536. XCTAssertNil(requestError)
  537. XCTAssertNil(downloadError)
  538. }
  539. func testThatValidationForRequestWithUnacceptableContentTypeResponseFails() {
  540. // Given
  541. let url = URL(string: "https://httpbin.org/xml")!
  542. var urlRequest = URLRequest(url: url)
  543. urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
  544. let expectation1 = expectation(description: "request should succeed and return xml")
  545. let expectation2 = expectation(description: "download should succeed and return xml")
  546. var requestError: AFError?
  547. var downloadError: AFError?
  548. // When
  549. AF.request(urlRequest).validate().response { resp in
  550. requestError = resp.error
  551. expectation1.fulfill()
  552. }
  553. AF.download(urlRequest).validate().response { resp in
  554. downloadError = resp.error
  555. expectation2.fulfill()
  556. }
  557. waitForExpectations(timeout: timeout, handler: nil)
  558. // Then
  559. XCTAssertNotNil(requestError)
  560. XCTAssertNotNil(downloadError)
  561. for error in [requestError, downloadError] {
  562. XCTAssertEqual(error?.isUnacceptableContentType, true)
  563. XCTAssertEqual(error?.responseContentType, "application/xml")
  564. XCTAssertEqual(error?.acceptableContentTypes?.first, "application/json")
  565. }
  566. }
  567. }
  568. // MARK: -
  569. private enum ValidationError: Error {
  570. case missingData, missingFile, fileReadFailed
  571. }
  572. extension DataRequest {
  573. func validateDataExists() -> Self {
  574. validate { _, _, data in
  575. guard data != nil else { return .failure(ValidationError.missingData) }
  576. return .success(())
  577. }
  578. }
  579. func validate(with error: Error) -> Self {
  580. validate { _, _, _ in .failure(error) }
  581. }
  582. }
  583. extension DownloadRequest {
  584. func validateDataExists() -> Self {
  585. validate { [unowned self] _, _, _ in
  586. let fileURL = self.fileURL
  587. guard let validFileURL = fileURL else { return .failure(ValidationError.missingFile) }
  588. do {
  589. _ = try Data(contentsOf: validFileURL)
  590. return .success(())
  591. } catch {
  592. return .failure(ValidationError.fileReadFailed)
  593. }
  594. }
  595. }
  596. func validate(with error: Error) -> Self {
  597. validate { _, _, _ in .failure(error) }
  598. }
  599. }
  600. // MARK: -
  601. final class CustomValidationTestCase: BaseTestCase {
  602. func testThatCustomValidationClosureHasAccessToServerResponseData() {
  603. // Given
  604. let urlString = "https://httpbin.org/get"
  605. let expectation1 = expectation(description: "request should return 200 status code")
  606. let expectation2 = expectation(description: "download should return 200 status code")
  607. var requestError: AFError?
  608. var downloadError: AFError?
  609. // When
  610. AF.request(urlString)
  611. .validate { _, _, data in
  612. guard data != nil else { return .failure(ValidationError.missingData) }
  613. return .success(())
  614. }
  615. .response { resp in
  616. requestError = resp.error
  617. expectation1.fulfill()
  618. }
  619. AF.download(urlString)
  620. .validate { _, _, fileURL in
  621. guard let fileURL = fileURL else { return .failure(ValidationError.missingFile) }
  622. do {
  623. _ = try Data(contentsOf: fileURL)
  624. return .success(())
  625. } catch {
  626. return .failure(ValidationError.fileReadFailed)
  627. }
  628. }
  629. .response { resp in
  630. downloadError = resp.error
  631. expectation2.fulfill()
  632. }
  633. waitForExpectations(timeout: timeout, handler: nil)
  634. // Then
  635. XCTAssertNil(requestError)
  636. XCTAssertNil(downloadError)
  637. }
  638. func testThatCustomValidationCanThrowCustomError() {
  639. // Given
  640. let urlString = "https://httpbin.org/get"
  641. let expectation1 = expectation(description: "request should return 200 status code")
  642. let expectation2 = expectation(description: "download should return 200 status code")
  643. var requestError: AFError?
  644. var downloadError: AFError?
  645. // When
  646. AF.request(urlString)
  647. .validate { _, _, _ in .failure(ValidationError.missingData) }
  648. .validate { _, _, _ in .failure(ValidationError.missingFile) } // should be ignored
  649. .response { resp in
  650. requestError = resp.error
  651. expectation1.fulfill()
  652. }
  653. AF.download(urlString)
  654. .validate { _, _, _ in .failure(ValidationError.missingFile) }
  655. .validate { _, _, _ in .failure(ValidationError.fileReadFailed) } // should be ignored
  656. .response { resp in
  657. downloadError = resp.error
  658. expectation2.fulfill()
  659. }
  660. waitForExpectations(timeout: timeout, handler: nil)
  661. // Then
  662. XCTAssertEqual(requestError?.asAFError?.underlyingError as? ValidationError, .missingData)
  663. XCTAssertEqual(downloadError?.asAFError?.underlyingError as? ValidationError, .missingFile)
  664. }
  665. func testThatValidationExtensionHasAccessToServerResponseData() {
  666. // Given
  667. let urlString = "https://httpbin.org/get"
  668. let expectation1 = expectation(description: "request should return 200 status code")
  669. let expectation2 = expectation(description: "download should return 200 status code")
  670. var requestError: AFError?
  671. var downloadError: AFError?
  672. // When
  673. AF.request(urlString)
  674. .validateDataExists()
  675. .response { resp in
  676. requestError = resp.error
  677. expectation1.fulfill()
  678. }
  679. AF.download(urlString)
  680. .validateDataExists()
  681. .response { resp in
  682. downloadError = resp.error
  683. expectation2.fulfill()
  684. }
  685. waitForExpectations(timeout: timeout, handler: nil)
  686. // Then
  687. XCTAssertNil(requestError)
  688. XCTAssertNil(downloadError)
  689. }
  690. func testThatValidationExtensionCanThrowCustomError() {
  691. // Given
  692. let urlString = "https://httpbin.org/get"
  693. let expectation1 = expectation(description: "request should return 200 status code")
  694. let expectation2 = expectation(description: "download should return 200 status code")
  695. var requestError: AFError?
  696. var downloadError: AFError?
  697. // When
  698. AF.request(urlString)
  699. .validate(with: ValidationError.missingData)
  700. .validate(with: ValidationError.missingFile) // should be ignored
  701. .response { resp in
  702. requestError = resp.error
  703. expectation1.fulfill()
  704. }
  705. AF.download(urlString)
  706. .validate(with: ValidationError.missingFile)
  707. .validate(with: ValidationError.fileReadFailed) // should be ignored
  708. .response { resp in
  709. downloadError = resp.error
  710. expectation2.fulfill()
  711. }
  712. waitForExpectations(timeout: timeout, handler: nil)
  713. // Then
  714. XCTAssertEqual(requestError?.asAFError?.underlyingError as? ValidationError, .missingData)
  715. XCTAssertEqual(downloadError?.asAFError?.underlyingError as? ValidationError, .missingFile)
  716. }
  717. }