ValidationTests.swift 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  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 testThatValidationForRequestWithNoAcceptableContentTypeResponseFails() {
  203. // Given
  204. let endpoint = Endpoint.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(endpoint)
  211. .validate(contentType: [])
  212. .response { resp in
  213. requestError = resp.error
  214. expectation1.fulfill()
  215. }
  216. AF.download(endpoint)
  217. .validate(contentType: [])
  218. .response { resp in
  219. downloadError = resp.error
  220. expectation2.fulfill()
  221. }
  222. waitForExpectations(timeout: timeout)
  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 endpoint = Endpoint.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(endpoint)
  241. .validate(contentType: [])
  242. .response { resp in
  243. requestError = resp.error
  244. expectation1.fulfill()
  245. }
  246. AF.download(endpoint)
  247. .validate(contentType: [])
  248. .response { resp in
  249. downloadError = resp.error
  250. expectation2.fulfill()
  251. }
  252. waitForExpectations(timeout: timeout)
  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 endpoint = Endpoint.method(.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(endpoint)
  320. .validate(contentType: ["*/*"])
  321. .response { resp in
  322. requestResponse = resp
  323. expectation1.fulfill()
  324. }
  325. manager.download(endpoint)
  326. .validate(contentType: ["*/*"])
  327. .response { resp in
  328. downloadResponse = resp
  329. expectation2.fulfill()
  330. }
  331. waitForExpectations(timeout: timeout)
  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 endpoint = Endpoint.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(endpoint)
  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(endpoint)
  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)
  370. // Then
  371. XCTAssertNil(requestError)
  372. XCTAssertNil(downloadError)
  373. }
  374. func testThatValidationForRequestWithUnacceptableStatusCodeAndContentTypeResponseFailsWithStatusCodeError() {
  375. // Given
  376. let endpoint = Endpoint.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(endpoint)
  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(endpoint)
  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)
  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 endpoint = Endpoint.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(endpoint)
  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(endpoint)
  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)
  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 urlRequest = Endpoint.ip.modifying(\.headers, to: [.accept("application/json")])
  443. let expectation1 = expectation(description: "request should succeed and return ip")
  444. let expectation2 = expectation(description: "download should succeed and return ip")
  445. var requestError: AFError?
  446. var downloadError: AFError?
  447. // When
  448. AF.request(urlRequest).validate().response { resp in
  449. requestError = resp.error
  450. expectation1.fulfill()
  451. }
  452. AF.download(urlRequest).validate().response { resp in
  453. downloadError = resp.error
  454. expectation2.fulfill()
  455. }
  456. waitForExpectations(timeout: timeout)
  457. // Then
  458. XCTAssertNil(requestError)
  459. XCTAssertNil(downloadError)
  460. }
  461. func testThatValidationForRequestWithUnacceptableStatusCodeResponseFails() {
  462. // Given
  463. let request = Endpoint.status(404)
  464. let expectation1 = expectation(description: "request should return 404 status code")
  465. let expectation2 = expectation(description: "download should return 404 status code")
  466. var requestError: AFError?
  467. var downloadError: AFError?
  468. // When
  469. AF.request(request)
  470. .validate()
  471. .response { resp in
  472. requestError = resp.error
  473. expectation1.fulfill()
  474. }
  475. AF.download(request)
  476. .validate()
  477. .response { resp in
  478. downloadError = resp.error
  479. expectation2.fulfill()
  480. }
  481. waitForExpectations(timeout: timeout)
  482. // Then
  483. XCTAssertNotNil(requestError)
  484. XCTAssertNotNil(downloadError)
  485. for error in [requestError, downloadError] {
  486. XCTAssertEqual(error?.isUnacceptableStatusCode, true)
  487. XCTAssertEqual(error?.responseCode, 404)
  488. }
  489. }
  490. func testThatValidationForRequestWithAcceptableWildcardContentTypeResponseSucceeds() {
  491. // Given
  492. let urlRequest = Endpoint.ip.modifying(\.headers, to: [.accept("application/*")])
  493. let expectation1 = expectation(description: "request should succeed and return ip")
  494. let expectation2 = expectation(description: "download should succeed and return ip")
  495. var requestError: AFError?
  496. var downloadError: AFError?
  497. // When
  498. AF.request(urlRequest).validate().response { resp in
  499. requestError = resp.error
  500. expectation1.fulfill()
  501. }
  502. AF.download(urlRequest).validate().response { resp in
  503. downloadError = resp.error
  504. expectation2.fulfill()
  505. }
  506. waitForExpectations(timeout: timeout)
  507. // Then
  508. XCTAssertNil(requestError)
  509. XCTAssertNil(downloadError)
  510. }
  511. func testThatValidationForRequestWithAcceptableComplexContentTypeResponseSucceeds() {
  512. // Given
  513. var urlRequest = Endpoint.xml.urlRequest
  514. let headerValue = "text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8,*/*;q=0.5"
  515. urlRequest.headers["Accept"] = headerValue
  516. let expectation1 = expectation(description: "request should succeed and return xml")
  517. let expectation2 = expectation(description: "request should succeed and return xml")
  518. var requestError: AFError?
  519. var downloadError: AFError?
  520. // When
  521. AF.request(urlRequest).validate().response { resp in
  522. requestError = resp.error
  523. expectation1.fulfill()
  524. }
  525. AF.download(urlRequest).validate().response { resp in
  526. downloadError = resp.error
  527. expectation2.fulfill()
  528. }
  529. waitForExpectations(timeout: timeout)
  530. // Then
  531. XCTAssertNil(requestError)
  532. XCTAssertNil(downloadError)
  533. }
  534. func testThatValidationForRequestWithUnacceptableContentTypeResponseFails() {
  535. // Given
  536. let urlRequest = Endpoint.xml.modifying(\.headers, to: [.accept("application/json")])
  537. let expectation1 = expectation(description: "request should succeed and return xml")
  538. let expectation2 = expectation(description: "download should succeed and return xml")
  539. var requestError: AFError?
  540. var downloadError: AFError?
  541. // When
  542. AF.request(urlRequest).validate().response { resp in
  543. requestError = resp.error
  544. expectation1.fulfill()
  545. }
  546. AF.download(urlRequest).validate().response { resp in
  547. downloadError = resp.error
  548. expectation2.fulfill()
  549. }
  550. waitForExpectations(timeout: timeout)
  551. // Then
  552. XCTAssertNotNil(requestError)
  553. XCTAssertNotNil(downloadError)
  554. for error in [requestError, downloadError] {
  555. XCTAssertEqual(error?.isUnacceptableContentType, true)
  556. XCTAssertEqual(error?.responseContentType, "application/xml")
  557. XCTAssertEqual(error?.acceptableContentTypes?.first, "application/json")
  558. }
  559. }
  560. }
  561. // MARK: -
  562. private enum ValidationError: Error {
  563. case missingData, missingFile, fileReadFailed
  564. }
  565. extension DataRequest {
  566. func validateDataExists() -> Self {
  567. validate { _, _, data in
  568. guard data != nil else { return .failure(ValidationError.missingData) }
  569. return .success(())
  570. }
  571. }
  572. func validate(with error: Error) -> Self {
  573. validate { _, _, _ in .failure(error) }
  574. }
  575. }
  576. extension DownloadRequest {
  577. func validateDataExists() -> Self {
  578. validate { [unowned self] _, _, _ in
  579. let fileURL = self.fileURL
  580. guard let validFileURL = fileURL else { return .failure(ValidationError.missingFile) }
  581. do {
  582. _ = try Data(contentsOf: validFileURL)
  583. return .success(())
  584. } catch {
  585. return .failure(ValidationError.fileReadFailed)
  586. }
  587. }
  588. }
  589. func validate(with error: Error) -> Self {
  590. validate { _, _, _ in .failure(error) }
  591. }
  592. }
  593. // MARK: -
  594. final class CustomValidationTestCase: BaseTestCase {
  595. func testThatCustomValidationClosureHasAccessToServerResponseData() {
  596. // Given
  597. let endpoint = Endpoint()
  598. let expectation1 = expectation(description: "request should return 200 status code")
  599. let expectation2 = expectation(description: "download should return 200 status code")
  600. var requestError: AFError?
  601. var downloadError: AFError?
  602. // When
  603. AF.request(endpoint)
  604. .validate { _, _, data in
  605. guard data != nil else { return .failure(ValidationError.missingData) }
  606. return .success(())
  607. }
  608. .response { resp in
  609. requestError = resp.error
  610. expectation1.fulfill()
  611. }
  612. AF.download(endpoint)
  613. .validate { _, _, fileURL in
  614. guard let fileURL = fileURL else { return .failure(ValidationError.missingFile) }
  615. do {
  616. _ = try Data(contentsOf: fileURL)
  617. return .success(())
  618. } catch {
  619. return .failure(ValidationError.fileReadFailed)
  620. }
  621. }
  622. .response { resp in
  623. downloadError = resp.error
  624. expectation2.fulfill()
  625. }
  626. waitForExpectations(timeout: timeout)
  627. // Then
  628. XCTAssertNil(requestError)
  629. XCTAssertNil(downloadError)
  630. }
  631. func testThatCustomValidationCanThrowCustomError() {
  632. // Given
  633. let endpoint = Endpoint()
  634. let expectation1 = expectation(description: "request should return 200 status code")
  635. let expectation2 = expectation(description: "download should return 200 status code")
  636. var requestError: AFError?
  637. var downloadError: AFError?
  638. // When
  639. AF.request(endpoint)
  640. .validate { _, _, _ in .failure(ValidationError.missingData) }
  641. .validate { _, _, _ in .failure(ValidationError.missingFile) } // should be ignored
  642. .response { resp in
  643. requestError = resp.error
  644. expectation1.fulfill()
  645. }
  646. AF.download(endpoint)
  647. .validate { _, _, _ in .failure(ValidationError.missingFile) }
  648. .validate { _, _, _ in .failure(ValidationError.fileReadFailed) } // should be ignored
  649. .response { resp in
  650. downloadError = resp.error
  651. expectation2.fulfill()
  652. }
  653. waitForExpectations(timeout: timeout)
  654. // Then
  655. XCTAssertEqual(requestError?.asAFError?.underlyingError as? ValidationError, .missingData)
  656. XCTAssertEqual(downloadError?.asAFError?.underlyingError as? ValidationError, .missingFile)
  657. }
  658. func testThatValidationExtensionHasAccessToServerResponseData() {
  659. // Given
  660. let endpoint = Endpoint()
  661. let expectation1 = expectation(description: "request should return 200 status code")
  662. let expectation2 = expectation(description: "download should return 200 status code")
  663. var requestError: AFError?
  664. var downloadError: AFError?
  665. // When
  666. AF.request(endpoint)
  667. .validateDataExists()
  668. .response { resp in
  669. requestError = resp.error
  670. expectation1.fulfill()
  671. }
  672. AF.download(endpoint)
  673. .validateDataExists()
  674. .response { resp in
  675. downloadError = resp.error
  676. expectation2.fulfill()
  677. }
  678. waitForExpectations(timeout: timeout)
  679. // Then
  680. XCTAssertNil(requestError)
  681. XCTAssertNil(downloadError)
  682. }
  683. func testThatValidationExtensionCanThrowCustomError() {
  684. // Given
  685. let endpoint = Endpoint()
  686. let expectation1 = expectation(description: "request should return 200 status code")
  687. let expectation2 = expectation(description: "download should return 200 status code")
  688. var requestError: AFError?
  689. var downloadError: AFError?
  690. // When
  691. AF.request(endpoint)
  692. .validate(with: ValidationError.missingData)
  693. .validate(with: ValidationError.missingFile) // should be ignored
  694. .response { resp in
  695. requestError = resp.error
  696. expectation1.fulfill()
  697. }
  698. AF.download(endpoint)
  699. .validate(with: ValidationError.missingFile)
  700. .validate(with: ValidationError.fileReadFailed) // should be ignored
  701. .response { resp in
  702. downloadError = resp.error
  703. expectation2.fulfill()
  704. }
  705. waitForExpectations(timeout: timeout)
  706. // Then
  707. XCTAssertEqual(requestError?.asAFError?.underlyingError as? ValidationError, .missingData)
  708. XCTAssertEqual(downloadError?.asAFError?.underlyingError as? ValidationError, .missingFile)
  709. }
  710. }