RequestTests.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. //
  2. // RequestTests.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. import Alamofire
  25. import Foundation
  26. import XCTest
  27. class RequestInitializationTestCase: BaseTestCase {
  28. func testRequestClassMethodWithMethodAndURL() {
  29. // Given
  30. let urlString = "https://httpbin.org/"
  31. // When
  32. let request = Alamofire.request(urlString, withMethod: .get)
  33. // Then
  34. XCTAssertNotNil(request.request)
  35. XCTAssertEqual(request.request?.httpMethod, "GET")
  36. XCTAssertEqual(request.request?.urlString, urlString)
  37. XCTAssertNil(request.response)
  38. }
  39. func testRequestClassMethodWithMethodAndURLAndParameters() {
  40. // Given
  41. let urlString = "https://httpbin.org/get"
  42. // When
  43. let request = Alamofire.request(urlString, withMethod: .get, parameters: ["foo": "bar"])
  44. // Then
  45. XCTAssertNotNil(request.request)
  46. XCTAssertEqual(request.request?.httpMethod, "GET")
  47. XCTAssertNotEqual(request.request?.urlString, urlString)
  48. XCTAssertEqual(request.request?.url?.query, "foo=bar")
  49. XCTAssertNil(request.response)
  50. }
  51. func testRequestClassMethodWithMethodURLParametersAndHeaders() {
  52. // Given
  53. let urlString = "https://httpbin.org/get"
  54. let headers = ["Authorization": "123456"]
  55. // When
  56. let request = Alamofire.request(urlString, withMethod: .get, parameters: ["foo": "bar"], headers: headers)
  57. // Then
  58. XCTAssertNotNil(request.request)
  59. XCTAssertEqual(request.request?.httpMethod, "GET")
  60. XCTAssertNotEqual(request.request?.urlString, urlString)
  61. XCTAssertEqual(request.request?.url?.query, "foo=bar")
  62. XCTAssertEqual(request.request?.value(forHTTPHeaderField: "Authorization"), "123456")
  63. XCTAssertNil(request.response)
  64. }
  65. }
  66. // MARK: -
  67. class RequestResponseTestCase: BaseTestCase {
  68. func testRequestResponse() {
  69. // Given
  70. let urlString = "https://httpbin.org/get"
  71. let expectation = self.expectation(description: "GET request should succeed: \(urlString)")
  72. var response: DefaultDataResponse?
  73. // When
  74. Alamofire.request(urlString, withMethod: .get, parameters: ["foo": "bar"])
  75. .response { resp in
  76. response = resp
  77. expectation.fulfill()
  78. }
  79. waitForExpectations(timeout: timeout, handler: nil)
  80. // Then
  81. XCTAssertNotNil(response?.request)
  82. XCTAssertNotNil(response?.response)
  83. XCTAssertNotNil(response?.data)
  84. XCTAssertNil(response?.error)
  85. }
  86. func testRequestResponseWithProgress() {
  87. // Given
  88. let randomBytes = 4 * 1024 * 1024
  89. let urlString = "https://httpbin.org/bytes/\(randomBytes)"
  90. let expectation = self.expectation(description: "Bytes download progress should be reported: \(urlString)")
  91. var byteValues: [(bytes: Int64, totalBytes: Int64, totalBytesExpected: Int64)] = []
  92. var progressValues: [(completedUnitCount: Int64, totalUnitCount: Int64)] = []
  93. var response: DefaultDataResponse?
  94. // When
  95. Alamofire.request(urlString, withMethod: .get)
  96. .downloadProgress { progress in
  97. progressValues.append((progress.completedUnitCount, progress.totalUnitCount))
  98. }
  99. .downloadProgress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
  100. let bytes = (bytes: bytesRead, totalBytes: totalBytesRead, totalBytesExpected: totalBytesExpectedToRead)
  101. byteValues.append(bytes)
  102. }
  103. .response { resp in
  104. response = resp
  105. expectation.fulfill()
  106. }
  107. waitForExpectations(timeout: timeout, handler: nil)
  108. // Then
  109. XCTAssertNotNil(response?.request)
  110. XCTAssertNotNil(response?.response)
  111. XCTAssertNotNil(response?.data)
  112. XCTAssertNil(response?.error)
  113. XCTAssertEqual(byteValues.count, progressValues.count)
  114. if byteValues.count == progressValues.count {
  115. for (byteValue, progressValue) in zip(byteValues, progressValues) {
  116. XCTAssertGreaterThan(byteValue.bytes, 0)
  117. XCTAssertEqual(byteValue.totalBytes, progressValue.completedUnitCount)
  118. XCTAssertEqual(byteValue.totalBytesExpected, progressValue.totalUnitCount)
  119. }
  120. }
  121. if let lastByteValue = byteValues.last, let lastProgressValue = progressValues.last {
  122. let byteValueFractionalCompletion = Double(lastByteValue.totalBytes) / Double(lastByteValue.totalBytesExpected)
  123. let progressValueFractionalCompletion = Double(lastProgressValue.0) / Double(lastProgressValue.1)
  124. XCTAssertEqual(byteValueFractionalCompletion, 1.0)
  125. XCTAssertEqual(progressValueFractionalCompletion, 1.0)
  126. } else {
  127. XCTFail("last item in bytesValues and progressValues should not be nil")
  128. }
  129. }
  130. func testRequestResponseWithStream() {
  131. // Given
  132. let randomBytes = 4 * 1024 * 1024
  133. let urlString = "https://httpbin.org/bytes/\(randomBytes)"
  134. let expectation = self.expectation(description: "Bytes download progress should be reported: \(urlString)")
  135. var byteValues: [(bytes: Int64, totalBytes: Int64, totalBytesExpected: Int64)] = []
  136. var progressValues: [(completedUnitCount: Int64, totalUnitCount: Int64)] = []
  137. var accumulatedData = [Data]()
  138. var response: DefaultDataResponse?
  139. // When
  140. Alamofire.request(urlString, withMethod: .get)
  141. .downloadProgress { progress in
  142. progressValues.append((progress.completedUnitCount, progress.totalUnitCount))
  143. }
  144. .downloadProgress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
  145. let bytes = (bytes: bytesRead, totalBytes: totalBytesRead, totalBytesExpected: totalBytesExpectedToRead)
  146. byteValues.append(bytes)
  147. }
  148. .stream { data in
  149. accumulatedData.append(data)
  150. }
  151. .response { resp in
  152. response = resp
  153. expectation.fulfill()
  154. }
  155. waitForExpectations(timeout: timeout, handler: nil)
  156. // Then
  157. XCTAssertNotNil(response?.request)
  158. XCTAssertNotNil(response?.response)
  159. XCTAssertNil(response?.data)
  160. XCTAssertNil(response?.error)
  161. XCTAssertGreaterThanOrEqual(accumulatedData.count, 1)
  162. XCTAssertEqual(byteValues.count, progressValues.count)
  163. if byteValues.count == progressValues.count {
  164. for (byteValue, progressValue) in zip(byteValues, progressValues) {
  165. XCTAssertGreaterThan(byteValue.bytes, 0)
  166. XCTAssertEqual(byteValue.totalBytes, progressValue.completedUnitCount)
  167. XCTAssertEqual(byteValue.totalBytesExpected, progressValue.totalUnitCount)
  168. }
  169. }
  170. if let lastByteValue = byteValues.last, let lastProgressValue = progressValues.last {
  171. let byteValueFractionalCompletion = Double(lastByteValue.totalBytes) / Double(lastByteValue.totalBytesExpected)
  172. let progressValueFractionalCompletion = Double(lastProgressValue.0) / Double(lastProgressValue.1)
  173. XCTAssertEqual(byteValueFractionalCompletion, 1.0)
  174. XCTAssertEqual(progressValueFractionalCompletion, 1.0)
  175. XCTAssertEqual(accumulatedData.reduce(Int64(0)) { $0 + $1.count }, lastByteValue.totalBytes)
  176. } else {
  177. XCTFail("last item in bytesValues and progressValues should not be nil")
  178. }
  179. }
  180. func testPOSTRequestWithUnicodeParameters() {
  181. // Given
  182. let urlString = "https://httpbin.org/post"
  183. let parameters = [
  184. "french": "français",
  185. "japanese": "日本語",
  186. "arabic": "العربية",
  187. "emoji": "😃"
  188. ]
  189. let expectation = self.expectation(description: "request should succeed")
  190. var response: DataResponse<Any>?
  191. // When
  192. Alamofire.request(urlString, withMethod: .post, parameters: parameters)
  193. .responseJSON { closureResponse in
  194. response = closureResponse
  195. expectation.fulfill()
  196. }
  197. waitForExpectations(timeout: timeout, handler: nil)
  198. // Then
  199. XCTAssertNotNil(response?.request)
  200. XCTAssertNotNil(response?.response)
  201. XCTAssertNotNil(response?.data)
  202. if let json = response?.result.value as? [String: Any], let form = json["form"] as? [String: String] {
  203. XCTAssertEqual(form["french"], parameters["french"])
  204. XCTAssertEqual(form["japanese"], parameters["japanese"])
  205. XCTAssertEqual(form["arabic"], parameters["arabic"])
  206. XCTAssertEqual(form["emoji"], parameters["emoji"])
  207. } else {
  208. XCTFail("form parameter in JSON should not be nil")
  209. }
  210. }
  211. func testPOSTRequestWithBase64EncodedImages() {
  212. // Given
  213. let urlString = "https://httpbin.org/post"
  214. let pngBase64EncodedString: String = {
  215. let URL = url(forResource: "unicorn", withExtension: "png")
  216. let data = try! Data(contentsOf: URL)
  217. return data.base64EncodedString(options: .lineLength64Characters)
  218. }()
  219. let jpegBase64EncodedString: String = {
  220. let URL = url(forResource: "rainbow", withExtension: "jpg")
  221. let data = try! Data(contentsOf: URL)
  222. return data.base64EncodedString(options: .lineLength64Characters)
  223. }()
  224. let parameters = [
  225. "email": "user@alamofire.org",
  226. "png_image": pngBase64EncodedString,
  227. "jpeg_image": jpegBase64EncodedString
  228. ]
  229. let expectation = self.expectation(description: "request should succeed")
  230. var response: DataResponse<Any>?
  231. // When
  232. Alamofire.request(urlString, withMethod: .post, parameters: parameters)
  233. .responseJSON { closureResponse in
  234. response = closureResponse
  235. expectation.fulfill()
  236. }
  237. waitForExpectations(timeout: timeout, handler: nil)
  238. // Then
  239. XCTAssertNotNil(response?.request)
  240. XCTAssertNotNil(response?.response)
  241. XCTAssertNotNil(response?.data)
  242. XCTAssertEqual(response?.result.isSuccess, true)
  243. if let json = response?.result.value as? [String: Any], let form = json["form"] as? [String: String] {
  244. XCTAssertEqual(form["email"], parameters["email"])
  245. XCTAssertEqual(form["png_image"], parameters["png_image"])
  246. XCTAssertEqual(form["jpeg_image"], parameters["jpeg_image"])
  247. } else {
  248. XCTFail("form parameter in JSON should not be nil")
  249. }
  250. }
  251. }
  252. // MARK: -
  253. extension Request {
  254. fileprivate func preValidate(operation: @escaping (Void) -> Void) -> Self {
  255. delegate.queue.addOperation {
  256. operation()
  257. }
  258. return self
  259. }
  260. fileprivate func postValidate(operation: @escaping (Void) -> Void) -> Self {
  261. delegate.queue.addOperation {
  262. operation()
  263. }
  264. return self
  265. }
  266. }
  267. // MARK: -
  268. class RequestExtensionTestCase: BaseTestCase {
  269. func testThatRequestExtensionHasAccessToTaskDelegateQueue() {
  270. // Given
  271. let urlString = "https://httpbin.org/get"
  272. let expectation = self.expectation(description: "GET request should succeed: \(urlString)")
  273. var responses: [String] = []
  274. // When
  275. Alamofire.request(urlString, withMethod: .get)
  276. .preValidate {
  277. responses.append("preValidate")
  278. }
  279. .validate()
  280. .postValidate {
  281. responses.append("postValidate")
  282. }
  283. .response { _ in
  284. responses.append("response")
  285. expectation.fulfill()
  286. }
  287. waitForExpectations(timeout: timeout, handler: nil)
  288. // Then
  289. if responses.count == 3 {
  290. XCTAssertEqual(responses[0], "preValidate")
  291. XCTAssertEqual(responses[1], "postValidate")
  292. XCTAssertEqual(responses[2], "response")
  293. } else {
  294. XCTFail("responses count should be equal to 3")
  295. }
  296. }
  297. }
  298. // MARK: -
  299. class RequestDescriptionTestCase: BaseTestCase {
  300. func testRequestDescription() {
  301. // Given
  302. let urlString = "https://httpbin.org/get"
  303. let request = Alamofire.request(urlString, withMethod: .get)
  304. let initialRequestDescription = request.description
  305. let expectation = self.expectation(description: "Request description should update: \(urlString)")
  306. var finalRequestDescription: String?
  307. var response: HTTPURLResponse?
  308. // When
  309. request.response { resp in
  310. finalRequestDescription = request.description
  311. response = resp.response
  312. expectation.fulfill()
  313. }
  314. waitForExpectations(timeout: timeout, handler: nil)
  315. // Then
  316. XCTAssertEqual(initialRequestDescription, "GET https://httpbin.org/get")
  317. XCTAssertEqual(finalRequestDescription, "GET https://httpbin.org/get (\(response?.statusCode ?? -1))")
  318. }
  319. }
  320. // MARK: -
  321. class RequestDebugDescriptionTestCase: BaseTestCase {
  322. // MARK: Properties
  323. let manager: SessionManager = {
  324. let manager = SessionManager(configuration: .default)
  325. manager.startRequestsImmediately = false
  326. return manager
  327. }()
  328. let managerWithAcceptLanguageHeader: SessionManager = {
  329. var headers = SessionManager.default.session.configuration.httpAdditionalHeaders ?? [:]
  330. headers["Accept-Language"] = "en-US"
  331. let configuration = URLSessionConfiguration.default
  332. configuration.httpAdditionalHeaders = headers
  333. let manager = SessionManager(configuration: configuration)
  334. manager.startRequestsImmediately = false
  335. return manager
  336. }()
  337. let managerWithContentTypeHeader: SessionManager = {
  338. var headers = SessionManager.default.session.configuration.httpAdditionalHeaders ?? [:]
  339. headers["Content-Type"] = "application/json"
  340. let configuration = URLSessionConfiguration.default
  341. configuration.httpAdditionalHeaders = headers
  342. let manager = SessionManager(configuration: configuration)
  343. manager.startRequestsImmediately = false
  344. return manager
  345. }()
  346. let managerDisallowingCookies: SessionManager = {
  347. let configuration = URLSessionConfiguration.default
  348. configuration.httpShouldSetCookies = false
  349. let manager = SessionManager(configuration: configuration)
  350. manager.startRequestsImmediately = false
  351. return manager
  352. }()
  353. // MARK: Tests
  354. func testGETRequestDebugDescription() {
  355. // Given
  356. let urlString = "https://httpbin.org/get"
  357. // When
  358. let request = manager.request(urlString, withMethod: .get)
  359. let components = cURLCommandComponents(for: request)
  360. // Then
  361. XCTAssertEqual(components[0..<3], ["$", "curl", "-i"])
  362. XCTAssertFalse(components.contains("-X"))
  363. XCTAssertEqual(components.last, "\"\(urlString)\"")
  364. }
  365. func testGETRequestWithDuplicateHeadersDebugDescription() {
  366. // Given
  367. let urlString = "https://httpbin.org/get"
  368. // When
  369. let headers = [ "Accept-Language": "en-GB" ]
  370. let request = managerWithAcceptLanguageHeader.request(urlString, withMethod: .get, headers: headers)
  371. let components = cURLCommandComponents(for: request)
  372. // Then
  373. XCTAssertEqual(components[0..<3], ["$", "curl", "-i"])
  374. XCTAssertFalse(components.contains("-X"))
  375. XCTAssertEqual(components.last, "\"\(urlString)\"")
  376. let tokens = request.debugDescription.components(separatedBy: "Accept-Language:")
  377. XCTAssertTrue(tokens.count == 2, "command should contain a single Accept-Language header")
  378. XCTAssertNotNil(request.debugDescription.range(of: "-H \"Accept-Language: en-GB\""))
  379. }
  380. func testPOSTRequestDebugDescription() {
  381. // Given
  382. let urlString = "https://httpbin.org/post"
  383. // When
  384. let request = manager.request(urlString, withMethod: .post)
  385. let components = cURLCommandComponents(for: request)
  386. // Then
  387. XCTAssertEqual(components[0..<3], ["$", "curl", "-i"])
  388. XCTAssertEqual(components[3..<5], ["-X", "POST"])
  389. XCTAssertEqual(components.last, "\"\(urlString)\"")
  390. }
  391. func testPOSTRequestWithJSONParametersDebugDescription() {
  392. // Given
  393. let urlString = "https://httpbin.org/post"
  394. let parameters = [
  395. "foo": "bar",
  396. "fo\"o": "b\"ar",
  397. "f'oo": "ba'r"
  398. ]
  399. // When
  400. let request = manager.request(urlString, withMethod: .post, parameters: parameters, encoding: .json)
  401. let components = cURLCommandComponents(for: request)
  402. // Then
  403. XCTAssertEqual(components[0..<3], ["$", "curl", "-i"])
  404. XCTAssertEqual(components[3..<5], ["-X", "POST"])
  405. XCTAssertNotNil(request.debugDescription.range(of: "-H \"Content-Type: application/json\""))
  406. XCTAssertNotNil(request.debugDescription.range(of: "-d \"{"))
  407. XCTAssertNotNil(request.debugDescription.range(of: "\\\"f'oo\\\":\\\"ba'r\\\""))
  408. XCTAssertNotNil(request.debugDescription.range(of: "\\\"fo\\\\\\\"o\\\":\\\"b\\\\\\\"ar\\\""))
  409. XCTAssertNotNil(request.debugDescription.range(of: "\\\"foo\\\":\\\"bar\\"))
  410. XCTAssertEqual(components.last, "\"\(urlString)\"")
  411. }
  412. func testPOSTRequestWithCookieDebugDescription() {
  413. // Given
  414. let urlString = "https://httpbin.org/post"
  415. let properties = [
  416. HTTPCookiePropertyKey.domain: "httpbin.org",
  417. HTTPCookiePropertyKey.path: "/post",
  418. HTTPCookiePropertyKey.name: "foo",
  419. HTTPCookiePropertyKey.value: "bar",
  420. ]
  421. let cookie = HTTPCookie(properties: properties)!
  422. manager.session.configuration.httpCookieStorage?.setCookie(cookie)
  423. // When
  424. let request = manager.request(urlString, withMethod: .post)
  425. let components = cURLCommandComponents(for: request)
  426. // Then
  427. XCTAssertEqual(components[0..<3], ["$", "curl", "-i"])
  428. XCTAssertEqual(components[3..<5], ["-X", "POST"])
  429. XCTAssertEqual(components.last, "\"\(urlString)\"")
  430. XCTAssertEqual(components[5..<6], ["-b"])
  431. }
  432. func testPOSTRequestWithCookiesDisabledDebugDescription() {
  433. // Given
  434. let urlString = "https://httpbin.org/post"
  435. let properties = [
  436. HTTPCookiePropertyKey.domain: "httpbin.org",
  437. HTTPCookiePropertyKey.path: "/post",
  438. HTTPCookiePropertyKey.name: "foo",
  439. HTTPCookiePropertyKey.value: "bar",
  440. ]
  441. let cookie = HTTPCookie(properties: properties)!
  442. managerDisallowingCookies.session.configuration.httpCookieStorage?.setCookie(cookie)
  443. // When
  444. let request = managerDisallowingCookies.request(urlString, withMethod: .post)
  445. let components = cURLCommandComponents(for: request)
  446. // Then
  447. let cookieComponents = components.filter { $0 == "-b" }
  448. XCTAssertTrue(cookieComponents.isEmpty)
  449. }
  450. func testMultipartFormDataRequestWithDuplicateHeadersDebugDescription() {
  451. // Given
  452. let urlString = "https://httpbin.org/post"
  453. let japaneseData = "日本語".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  454. let expectation = self.expectation(description: "multipart form data encoding should succeed")
  455. var request: Request?
  456. var components: [String] = []
  457. // When
  458. managerWithContentTypeHeader.upload(
  459. multipartFormData: { multipartFormData in
  460. multipartFormData.append(japaneseData, withName: "japanese")
  461. },
  462. to: urlString,
  463. withMethod: .post,
  464. encodingCompletion: { result in
  465. switch result {
  466. case .success(let upload, _, _):
  467. request = upload
  468. components = self.cURLCommandComponents(for: upload)
  469. expectation.fulfill()
  470. case .failure:
  471. expectation.fulfill()
  472. }
  473. }
  474. )
  475. waitForExpectations(timeout: timeout, handler: nil)
  476. debugPrint(request!)
  477. // Then
  478. XCTAssertEqual(components[0..<3], ["$", "curl", "-i"])
  479. XCTAssertTrue(components.contains("-X"))
  480. XCTAssertEqual(components.last, "\"\(urlString)\"")
  481. let tokens = request.debugDescription.components(separatedBy: "Content-Type:")
  482. XCTAssertTrue(tokens.count == 2, "command should contain a single Content-Type header")
  483. XCTAssertNotNil(request.debugDescription.range(of: "-H \"Content-Type: multipart/form-data;"))
  484. }
  485. func testThatRequestWithInvalidURLDebugDescription() {
  486. // Given
  487. let urlString = "invalid_url"
  488. // When
  489. let request = manager.request(urlString, withMethod: .get)
  490. let debugDescription = request.debugDescription
  491. // Then
  492. XCTAssertNotNil(debugDescription, "debugDescription should not crash")
  493. }
  494. // MARK: Test Helper Methods
  495. private func cURLCommandComponents(for request: Request) -> [String] {
  496. let whitespaceCharacterSet = CharacterSet.whitespacesAndNewlines
  497. return request.debugDescription
  498. .components(separatedBy: whitespaceCharacterSet)
  499. .filter { $0 != "" && $0 != "\\" }
  500. }
  501. }