RequestTests.swift 23 KB

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