ValidationTests.swift 33 KB

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