ValidationTests.swift 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965
  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 endpoint = Endpoint.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(endpoint)
  37. .validate(statusCode: 200..<300)
  38. .response { resp in
  39. requestError = resp.error
  40. expectation1.fulfill()
  41. }
  42. AF.download(endpoint)
  43. .validate(statusCode: 200..<300)
  44. .response { resp in
  45. downloadError = resp.error
  46. expectation2.fulfill()
  47. }
  48. waitForExpectations(timeout: timeout)
  49. // Then
  50. XCTAssertNil(requestError)
  51. XCTAssertNil(downloadError)
  52. }
  53. func testThatValidationForRequestWithUnacceptableStatusCodeResponseFails() {
  54. // Given
  55. let endpoint = Endpoint.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(endpoint)
  62. .validate(statusCode: [200])
  63. .response { resp in
  64. requestError = resp.error
  65. expectation1.fulfill()
  66. }
  67. AF.download(endpoint)
  68. .validate(statusCode: [200])
  69. .response { resp in
  70. downloadError = resp.error
  71. expectation2.fulfill()
  72. }
  73. waitForExpectations(timeout: timeout)
  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 endpoint = Endpoint.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(endpoint)
  91. .validate(statusCode: [])
  92. .response { resp in
  93. requestError = resp.error
  94. expectation1.fulfill()
  95. }
  96. AF.download(endpoint)
  97. .validate(statusCode: [])
  98. .response { resp in
  99. downloadError = resp.error
  100. expectation2.fulfill()
  101. }
  102. waitForExpectations(timeout: timeout)
  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 endpoint = Endpoint.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(endpoint)
  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(endpoint)
  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)
  139. // Then
  140. XCTAssertNil(requestError)
  141. XCTAssertNil(downloadError)
  142. }
  143. func testThatValidationForRequestWithAcceptableWildcardContentTypeResponseSucceeds() {
  144. // Given
  145. let endpoint = Endpoint.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(endpoint)
  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(endpoint)
  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)
  168. // Then
  169. XCTAssertNil(requestError)
  170. XCTAssertNil(downloadError)
  171. }
  172. func testThatValidationForRequestWithUnacceptableContentTypeResponseFails() {
  173. // Given
  174. let endpoint = Endpoint.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(endpoint)
  181. .validate(contentType: ["application/octet-stream"])
  182. .response { resp in
  183. requestError = resp.error
  184. expectation1.fulfill()
  185. }
  186. AF.download(endpoint)
  187. .validate(contentType: ["application/octet-stream"])
  188. .response { resp in
  189. downloadError = resp.error
  190. expectation2.fulfill()
  191. }
  192. waitForExpectations(timeout: timeout)
  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 testThatContentTypeValidationFailureSortsPossibleContentTypes() {
  203. // Given
  204. let endpoint = Endpoint.xml
  205. let requestDidCompleteExpectation = expectation(description: "request should succeed and return xml")
  206. let downloadDidCompleteExpectation = expectation(description: "download should succeed and return xml")
  207. var requestError: AFError?
  208. var downloadError: AFError?
  209. let acceptableContentTypes = [ // Sorted in a random order, not alphabetically
  210. "application/octet-stream",
  211. "image/gif",
  212. "image/x-xbitmap",
  213. "image/tiff",
  214. "image/jpg",
  215. "image/x-bmp",
  216. "image/jpeg",
  217. "image/x-icon",
  218. "image/jp2",
  219. "image/png",
  220. "image/ico",
  221. "image/bmp",
  222. "image/x-ms-bmp",
  223. "image/x-win-bitmap"
  224. ]
  225. // When
  226. AF.request(endpoint)
  227. .validate(contentType: acceptableContentTypes)
  228. .response { resp in
  229. requestError = resp.error
  230. requestDidCompleteExpectation.fulfill()
  231. }
  232. AF.download(endpoint)
  233. .validate(contentType: acceptableContentTypes)
  234. .response { resp in
  235. downloadError = resp.error
  236. downloadDidCompleteExpectation.fulfill()
  237. }
  238. waitForExpectations(timeout: timeout)
  239. // Then
  240. XCTAssertNotNil(requestError)
  241. XCTAssertNotNil(downloadError)
  242. let expectedAcceptableContentTypes = [ // Sorted in a specific order, alphabetically
  243. "application/octet-stream",
  244. "image/bmp",
  245. "image/gif",
  246. "image/ico",
  247. "image/jp2",
  248. "image/jpeg",
  249. "image/jpg",
  250. "image/png",
  251. "image/tiff",
  252. "image/x-bmp",
  253. "image/x-icon",
  254. "image/x-ms-bmp",
  255. "image/x-win-bitmap",
  256. "image/x-xbitmap"
  257. ]
  258. for error in [requestError, downloadError] {
  259. XCTAssertEqual(error?.isUnacceptableContentType, true)
  260. XCTAssertEqual(error?.responseContentType, "application/xml")
  261. XCTAssertEqual(error?.acceptableContentTypes, expectedAcceptableContentTypes)
  262. }
  263. }
  264. func testThatValidationForRequestWithNoAcceptableContentTypeResponseFails() {
  265. // Given
  266. let endpoint = Endpoint.xml
  267. let expectation1 = expectation(description: "request should succeed and return xml")
  268. let expectation2 = expectation(description: "download should succeed and return xml")
  269. var requestError: AFError?
  270. var downloadError: AFError?
  271. // When
  272. AF.request(endpoint)
  273. .validate(contentType: [])
  274. .response { resp in
  275. requestError = resp.error
  276. expectation1.fulfill()
  277. }
  278. AF.download(endpoint)
  279. .validate(contentType: [])
  280. .response { resp in
  281. downloadError = resp.error
  282. expectation2.fulfill()
  283. }
  284. waitForExpectations(timeout: timeout)
  285. // Then
  286. XCTAssertNotNil(requestError)
  287. XCTAssertNotNil(downloadError)
  288. for error in [requestError, downloadError] {
  289. XCTAssertEqual(error?.isUnacceptableContentType, true)
  290. XCTAssertEqual(error?.responseContentType, "application/xml")
  291. XCTAssertEqual(error?.acceptableContentTypes?.isEmpty, true)
  292. }
  293. }
  294. func testThatValidationForRequestWithNoAcceptableContentTypeResponseSucceedsWhenNoDataIsReturned() {
  295. // Given
  296. let endpoint = Endpoint.status(204)
  297. let expectation1 = expectation(description: "request should succeed and return no data")
  298. let expectation2 = expectation(description: "download should succeed and return no data")
  299. var requestError: AFError?
  300. var downloadError: AFError?
  301. // When
  302. AF.request(endpoint)
  303. .validate(contentType: [])
  304. .response { resp in
  305. requestError = resp.error
  306. expectation1.fulfill()
  307. }
  308. AF.download(endpoint)
  309. .validate(contentType: [])
  310. .response { resp in
  311. downloadError = resp.error
  312. expectation2.fulfill()
  313. }
  314. waitForExpectations(timeout: timeout)
  315. // Then
  316. XCTAssertNil(requestError)
  317. XCTAssertNil(downloadError)
  318. }
  319. func testThatValidationForRequestWithAcceptableWildcardContentTypeResponseSucceedsWhenResponseIsNil() {
  320. // Given
  321. class MockManager: Session {
  322. override func request(_ convertible: URLRequestConvertible,
  323. interceptor: RequestInterceptor? = nil) -> DataRequest {
  324. let request = MockDataRequest(convertible: convertible,
  325. underlyingQueue: rootQueue,
  326. serializationQueue: serializationQueue,
  327. eventMonitor: eventMonitor,
  328. interceptor: interceptor,
  329. delegate: self)
  330. perform(request)
  331. return request
  332. }
  333. override func download(_ convertible: URLRequestConvertible,
  334. interceptor: RequestInterceptor? = nil,
  335. to destination: DownloadRequest.Destination?)
  336. -> DownloadRequest {
  337. let request = MockDownloadRequest(downloadable: .request(convertible),
  338. underlyingQueue: rootQueue,
  339. serializationQueue: serializationQueue,
  340. eventMonitor: eventMonitor,
  341. interceptor: interceptor,
  342. delegate: self,
  343. destination: destination ?? MockDownloadRequest.defaultDestination)
  344. perform(request)
  345. return request
  346. }
  347. }
  348. class MockDataRequest: DataRequest {
  349. override var response: HTTPURLResponse? {
  350. MockHTTPURLResponse(url: request!.url!,
  351. statusCode: 204,
  352. httpVersion: "HTTP/1.1",
  353. headerFields: nil)
  354. }
  355. }
  356. class MockDownloadRequest: DownloadRequest {
  357. override var response: HTTPURLResponse? {
  358. MockHTTPURLResponse(url: request!.url!,
  359. statusCode: 204,
  360. httpVersion: "HTTP/1.1",
  361. headerFields: nil)
  362. }
  363. }
  364. class MockHTTPURLResponse: HTTPURLResponse {
  365. override var mimeType: String? { nil }
  366. }
  367. let manager: Session = {
  368. let configuration: URLSessionConfiguration = {
  369. let configuration = URLSessionConfiguration.ephemeral
  370. configuration.headers = HTTPHeaders.default
  371. return configuration
  372. }()
  373. return MockManager(configuration: configuration)
  374. }()
  375. let endpoint = Endpoint.method(.delete)
  376. let expectation1 = expectation(description: "request should be stubbed and return 204 status code")
  377. let expectation2 = expectation(description: "download should be stubbed and return 204 status code")
  378. var requestResponse: DataResponse<Data?, AFError>?
  379. var downloadResponse: DownloadResponse<URL?, AFError>?
  380. // When
  381. manager.request(endpoint)
  382. .validate(contentType: ["*/*"])
  383. .response { resp in
  384. requestResponse = resp
  385. expectation1.fulfill()
  386. }
  387. manager.download(endpoint)
  388. .validate(contentType: ["*/*"])
  389. .response { resp in
  390. downloadResponse = resp
  391. expectation2.fulfill()
  392. }
  393. waitForExpectations(timeout: timeout)
  394. // Then
  395. XCTAssertNotNil(requestResponse?.response)
  396. XCTAssertNotNil(requestResponse?.data)
  397. XCTAssertNil(requestResponse?.error)
  398. XCTAssertEqual(requestResponse?.response?.statusCode, 204)
  399. XCTAssertNil(requestResponse?.response?.mimeType)
  400. XCTAssertNotNil(downloadResponse?.response)
  401. XCTAssertNotNil(downloadResponse?.fileURL)
  402. XCTAssertNil(downloadResponse?.error)
  403. XCTAssertEqual(downloadResponse?.response?.statusCode, 204)
  404. XCTAssertNil(downloadResponse?.response?.mimeType)
  405. }
  406. }
  407. // MARK: -
  408. final class MultipleValidationTestCase: BaseTestCase {
  409. func testThatValidationForRequestWithAcceptableStatusCodeAndContentTypeResponseSucceeds() {
  410. // Given
  411. let endpoint = Endpoint.ip
  412. let expectation1 = expectation(description: "request should succeed and return ip")
  413. let expectation2 = expectation(description: "request should succeed and return ip")
  414. var requestError: AFError?
  415. var downloadError: AFError?
  416. // When
  417. AF.request(endpoint)
  418. .validate(statusCode: 200..<300)
  419. .validate(contentType: ["application/json"])
  420. .response { resp in
  421. requestError = resp.error
  422. expectation1.fulfill()
  423. }
  424. AF.download(endpoint)
  425. .validate(statusCode: 200..<300)
  426. .validate(contentType: ["application/json"])
  427. .response { resp in
  428. downloadError = resp.error
  429. expectation2.fulfill()
  430. }
  431. waitForExpectations(timeout: timeout)
  432. // Then
  433. XCTAssertNil(requestError)
  434. XCTAssertNil(downloadError)
  435. }
  436. func testThatValidationForRequestWithUnacceptableStatusCodeAndContentTypeResponseFailsWithStatusCodeError() {
  437. // Given
  438. let endpoint = Endpoint.xml
  439. let expectation1 = expectation(description: "request should succeed and return xml")
  440. let expectation2 = expectation(description: "download should succeed and return xml")
  441. var requestError: AFError?
  442. var downloadError: AFError?
  443. // When
  444. AF.request(endpoint)
  445. .validate(statusCode: 400..<600)
  446. .validate(contentType: ["application/octet-stream"])
  447. .response { resp in
  448. requestError = resp.error
  449. expectation1.fulfill()
  450. }
  451. AF.download(endpoint)
  452. .validate(statusCode: 400..<600)
  453. .validate(contentType: ["application/octet-stream"])
  454. .response { resp in
  455. downloadError = resp.error
  456. expectation2.fulfill()
  457. }
  458. waitForExpectations(timeout: timeout)
  459. // Then
  460. XCTAssertNotNil(requestError)
  461. XCTAssertNotNil(downloadError)
  462. for error in [requestError, downloadError] {
  463. XCTAssertEqual(error?.isUnacceptableStatusCode, true)
  464. XCTAssertEqual(error?.responseCode, 200)
  465. }
  466. }
  467. func testThatValidationForRequestWithUnacceptableStatusCodeAndContentTypeResponseFailsWithContentTypeError() {
  468. // Given
  469. let endpoint = Endpoint.xml
  470. let expectation1 = expectation(description: "request should succeed and return xml")
  471. let expectation2 = expectation(description: "download should succeed and return xml")
  472. var requestError: AFError?
  473. var downloadError: AFError?
  474. // When
  475. AF.request(endpoint)
  476. .validate(contentType: ["application/octet-stream"])
  477. .validate(statusCode: 400..<600)
  478. .response { resp in
  479. requestError = resp.error
  480. expectation1.fulfill()
  481. }
  482. AF.download(endpoint)
  483. .validate(contentType: ["application/octet-stream"])
  484. .validate(statusCode: 400..<600)
  485. .response { resp in
  486. downloadError = resp.error
  487. expectation2.fulfill()
  488. }
  489. waitForExpectations(timeout: timeout)
  490. // Then
  491. XCTAssertNotNil(requestError)
  492. XCTAssertNotNil(downloadError)
  493. for error in [requestError, downloadError] {
  494. XCTAssertEqual(error?.isUnacceptableContentType, true)
  495. XCTAssertEqual(error?.responseContentType, "application/xml")
  496. XCTAssertEqual(error?.acceptableContentTypes?.first, "application/octet-stream")
  497. }
  498. }
  499. }
  500. // MARK: -
  501. final class AutomaticValidationTestCase: BaseTestCase {
  502. func testThatValidationForRequestWithAcceptableStatusCodeAndContentTypeResponseSucceeds() {
  503. // Given
  504. let urlRequest = Endpoint.ip.modifying(\.headers, to: [.accept("application/json")])
  505. let expectation1 = expectation(description: "request should succeed and return ip")
  506. let expectation2 = expectation(description: "download should succeed and return ip")
  507. var requestError: AFError?
  508. var downloadError: AFError?
  509. // When
  510. AF.request(urlRequest).validate().response { resp in
  511. requestError = resp.error
  512. expectation1.fulfill()
  513. }
  514. AF.download(urlRequest).validate().response { resp in
  515. downloadError = resp.error
  516. expectation2.fulfill()
  517. }
  518. waitForExpectations(timeout: timeout)
  519. // Then
  520. XCTAssertNil(requestError)
  521. XCTAssertNil(downloadError)
  522. }
  523. func testThatValidationForRequestWithUnacceptableStatusCodeResponseFails() {
  524. // Given
  525. let request = Endpoint.status(404)
  526. let expectation1 = expectation(description: "request should return 404 status code")
  527. let expectation2 = expectation(description: "download should return 404 status code")
  528. var requestError: AFError?
  529. var downloadError: AFError?
  530. // When
  531. AF.request(request)
  532. .validate()
  533. .response { resp in
  534. requestError = resp.error
  535. expectation1.fulfill()
  536. }
  537. AF.download(request)
  538. .validate()
  539. .response { resp in
  540. downloadError = resp.error
  541. expectation2.fulfill()
  542. }
  543. waitForExpectations(timeout: timeout)
  544. // Then
  545. XCTAssertNotNil(requestError)
  546. XCTAssertNotNil(downloadError)
  547. for error in [requestError, downloadError] {
  548. XCTAssertEqual(error?.isUnacceptableStatusCode, true)
  549. XCTAssertEqual(error?.responseCode, 404)
  550. }
  551. }
  552. func testThatValidationForRequestWithAcceptableWildcardContentTypeResponseSucceeds() {
  553. // Given
  554. let urlRequest = Endpoint.ip.modifying(\.headers, to: [.accept("application/*")])
  555. let expectation1 = expectation(description: "request should succeed and return ip")
  556. let expectation2 = expectation(description: "download should succeed and return ip")
  557. var requestError: AFError?
  558. var downloadError: AFError?
  559. // When
  560. AF.request(urlRequest).validate().response { resp in
  561. requestError = resp.error
  562. expectation1.fulfill()
  563. }
  564. AF.download(urlRequest).validate().response { resp in
  565. downloadError = resp.error
  566. expectation2.fulfill()
  567. }
  568. waitForExpectations(timeout: timeout)
  569. // Then
  570. XCTAssertNil(requestError)
  571. XCTAssertNil(downloadError)
  572. }
  573. func testThatValidationForRequestWithAcceptableComplexContentTypeResponseSucceeds() {
  574. // Given
  575. var urlRequest = Endpoint.xml.urlRequest
  576. let headerValue = "text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8,*/*;q=0.5"
  577. urlRequest.headers["Accept"] = headerValue
  578. let expectation1 = expectation(description: "request should succeed and return xml")
  579. let expectation2 = expectation(description: "request should succeed and return xml")
  580. var requestError: AFError?
  581. var downloadError: AFError?
  582. // When
  583. AF.request(urlRequest).validate().response { resp in
  584. requestError = resp.error
  585. expectation1.fulfill()
  586. }
  587. AF.download(urlRequest).validate().response { resp in
  588. downloadError = resp.error
  589. expectation2.fulfill()
  590. }
  591. waitForExpectations(timeout: timeout)
  592. // Then
  593. XCTAssertNil(requestError)
  594. XCTAssertNil(downloadError)
  595. }
  596. func testThatValidationForRequestWithUnacceptableContentTypeResponseFails() {
  597. // Given
  598. let urlRequest = Endpoint.xml.modifying(\.headers, to: [.accept("application/json")])
  599. let expectation1 = expectation(description: "request should succeed and return xml")
  600. let expectation2 = expectation(description: "download should succeed and return xml")
  601. var requestError: AFError?
  602. var downloadError: AFError?
  603. // When
  604. AF.request(urlRequest).validate().response { resp in
  605. requestError = resp.error
  606. expectation1.fulfill()
  607. }
  608. AF.download(urlRequest).validate().response { resp in
  609. downloadError = resp.error
  610. expectation2.fulfill()
  611. }
  612. waitForExpectations(timeout: timeout)
  613. // Then
  614. XCTAssertNotNil(requestError)
  615. XCTAssertNotNil(downloadError)
  616. for error in [requestError, downloadError] {
  617. XCTAssertEqual(error?.isUnacceptableContentType, true)
  618. XCTAssertEqual(error?.responseContentType, "application/xml")
  619. XCTAssertEqual(error?.acceptableContentTypes?.first, "application/json")
  620. }
  621. }
  622. }
  623. // MARK: -
  624. private enum ValidationError: Error {
  625. case missingData, missingFile, fileReadFailed
  626. }
  627. extension DataRequest {
  628. func validateDataExists() -> Self {
  629. validate { _, _, data in
  630. guard data != nil else { return .failure(ValidationError.missingData) }
  631. return .success(())
  632. }
  633. }
  634. func validate(with error: Error) -> Self {
  635. validate { _, _, _ in .failure(error) }
  636. }
  637. }
  638. extension DownloadRequest {
  639. func validateDataExists() -> Self {
  640. validate { [unowned self] _, _, _ in
  641. guard let validFileURL = fileURL else { return .failure(ValidationError.missingFile) }
  642. do {
  643. _ = try Data(contentsOf: validFileURL)
  644. return .success(())
  645. } catch {
  646. return .failure(ValidationError.fileReadFailed)
  647. }
  648. }
  649. }
  650. func validate(with error: Error) -> Self {
  651. validate { _, _, _ in .failure(error) }
  652. }
  653. }
  654. // MARK: -
  655. final class CustomValidationTestCase: BaseTestCase {
  656. func testThatCustomValidationClosureHasAccessToServerResponseData() {
  657. // Given
  658. let endpoint = Endpoint()
  659. let expectation1 = expectation(description: "request should return 200 status code")
  660. let expectation2 = expectation(description: "download should return 200 status code")
  661. var requestError: AFError?
  662. var downloadError: AFError?
  663. // When
  664. AF.request(endpoint)
  665. .validate { _, _, data in
  666. guard data != nil else { return .failure(ValidationError.missingData) }
  667. return .success(())
  668. }
  669. .response { resp in
  670. requestError = resp.error
  671. expectation1.fulfill()
  672. }
  673. AF.download(endpoint)
  674. .validate { _, _, fileURL in
  675. guard let fileURL = fileURL else { return .failure(ValidationError.missingFile) }
  676. do {
  677. _ = try Data(contentsOf: fileURL)
  678. return .success(())
  679. } catch {
  680. return .failure(ValidationError.fileReadFailed)
  681. }
  682. }
  683. .response { resp in
  684. downloadError = resp.error
  685. expectation2.fulfill()
  686. }
  687. waitForExpectations(timeout: timeout)
  688. // Then
  689. XCTAssertNil(requestError)
  690. XCTAssertNil(downloadError)
  691. }
  692. func testThatCustomValidationCanThrowCustomError() {
  693. // Given
  694. let endpoint = Endpoint()
  695. let expectation1 = expectation(description: "request should return 200 status code")
  696. let expectation2 = expectation(description: "download should return 200 status code")
  697. var requestError: AFError?
  698. var downloadError: AFError?
  699. // When
  700. AF.request(endpoint)
  701. .validate { _, _, _ in .failure(ValidationError.missingData) }
  702. .validate { _, _, _ in .failure(ValidationError.missingFile) } // should be ignored
  703. .response { resp in
  704. requestError = resp.error
  705. expectation1.fulfill()
  706. }
  707. AF.download(endpoint)
  708. .validate { _, _, _ in .failure(ValidationError.missingFile) }
  709. .validate { _, _, _ in .failure(ValidationError.fileReadFailed) } // should be ignored
  710. .response { resp in
  711. downloadError = resp.error
  712. expectation2.fulfill()
  713. }
  714. waitForExpectations(timeout: timeout)
  715. // Then
  716. XCTAssertEqual(requestError?.asAFError?.underlyingError as? ValidationError, .missingData)
  717. XCTAssertEqual(downloadError?.asAFError?.underlyingError as? ValidationError, .missingFile)
  718. }
  719. func testThatValidationExtensionHasAccessToServerResponseData() {
  720. // Given
  721. let endpoint = Endpoint()
  722. let expectation1 = expectation(description: "request should return 200 status code")
  723. let expectation2 = expectation(description: "download should return 200 status code")
  724. var requestError: AFError?
  725. var downloadError: AFError?
  726. // When
  727. AF.request(endpoint)
  728. .validateDataExists()
  729. .response { resp in
  730. requestError = resp.error
  731. expectation1.fulfill()
  732. }
  733. AF.download(endpoint)
  734. .validateDataExists()
  735. .response { resp in
  736. downloadError = resp.error
  737. expectation2.fulfill()
  738. }
  739. waitForExpectations(timeout: timeout)
  740. // Then
  741. XCTAssertNil(requestError)
  742. XCTAssertNil(downloadError)
  743. }
  744. func testThatValidationExtensionCanThrowCustomError() {
  745. // Given
  746. let endpoint = Endpoint()
  747. let expectation1 = expectation(description: "request should return 200 status code")
  748. let expectation2 = expectation(description: "download should return 200 status code")
  749. var requestError: AFError?
  750. var downloadError: AFError?
  751. // When
  752. AF.request(endpoint)
  753. .validate(with: ValidationError.missingData)
  754. .validate(with: ValidationError.missingFile) // should be ignored
  755. .response { resp in
  756. requestError = resp.error
  757. expectation1.fulfill()
  758. }
  759. AF.download(endpoint)
  760. .validate(with: ValidationError.missingFile)
  761. .validate(with: ValidationError.fileReadFailed) // should be ignored
  762. .response { resp in
  763. downloadError = resp.error
  764. expectation2.fulfill()
  765. }
  766. waitForExpectations(timeout: timeout)
  767. // Then
  768. XCTAssertEqual(requestError?.asAFError?.underlyingError as? ValidationError, .missingData)
  769. XCTAssertEqual(downloadError?.asAFError?.underlyingError as? ValidationError, .missingFile)
  770. }
  771. }