ParameterEncoderTests.swift 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. //
  2. // ParameterEncoderTests.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. import Alamofire
  25. import XCTest
  26. final class JSONParameterEncoderTests: BaseTestCase {
  27. func testThatDataIsProperlyEncodedAndProperContentTypeIsSet() throws {
  28. // Given
  29. let encoder = JSONParameterEncoder()
  30. let request = URLRequest.makeHTTPBinRequest()
  31. // When
  32. let newRequest = try encoder.encode(HTTPBinParameters.default, into: request)
  33. // Then
  34. XCTAssertEqual(newRequest.headers["Content-Type"], "application/json")
  35. XCTAssertEqual(newRequest.httpBody?.asString, "{\"property\":\"property\"}")
  36. }
  37. func testThatDataIsProperlyEncodedButContentTypeIsNotSetIfRequestAlreadyHasAContentType() throws {
  38. // Given
  39. let encoder = JSONParameterEncoder()
  40. var request = URLRequest.makeHTTPBinRequest()
  41. request.headers.update(.contentType("type"))
  42. // When
  43. let newRequest = try encoder.encode(HTTPBinParameters.default, into: request)
  44. // Then
  45. XCTAssertEqual(newRequest.headers["Content-Type"], "type")
  46. XCTAssertEqual(newRequest.httpBody?.asString, "{\"property\":\"property\"}")
  47. }
  48. func testThatJSONEncoderCanBeCustomized() throws {
  49. // Given
  50. let jsonEncoder = JSONEncoder()
  51. jsonEncoder.outputFormatting = .prettyPrinted
  52. let encoder = JSONParameterEncoder(encoder: jsonEncoder)
  53. let request = URLRequest.makeHTTPBinRequest()
  54. // When
  55. let newRequest = try encoder.encode(HTTPBinParameters.default, into: request)
  56. // Then
  57. let expected = """
  58. {
  59. "property" : "property"
  60. }
  61. """
  62. XCTAssertEqual(newRequest.httpBody?.asString, expected)
  63. }
  64. func testThatJSONEncoderDefaultWorks() throws {
  65. // Given
  66. let encoder = JSONParameterEncoder.default
  67. let request = URLRequest.makeHTTPBinRequest()
  68. // When
  69. let encoded = try encoder.encode(HTTPBinParameters.default, into: request)
  70. // Then
  71. let expected = """
  72. {"property":"property"}
  73. """
  74. XCTAssertEqual(encoded.httpBody?.asString, expected)
  75. }
  76. func testThatJSONEncoderPrettyPrintedPrintsPretty() throws {
  77. // Given
  78. let encoder = JSONParameterEncoder.prettyPrinted
  79. let request = URLRequest.makeHTTPBinRequest()
  80. // When
  81. let encoded = try encoder.encode(HTTPBinParameters.default, into: request)
  82. // Then
  83. let expected = """
  84. {
  85. "property" : "property"
  86. }
  87. """
  88. XCTAssertEqual(encoded.httpBody?.asString, expected)
  89. }
  90. }
  91. final class SortedKeysJSONParameterEncoderTests: BaseTestCase {
  92. func testTestJSONEncoderSortedKeysHasSortedKeys() throws {
  93. guard #available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) else { return }
  94. // Given
  95. let encoder = JSONParameterEncoder.sortedKeys
  96. let request = URLRequest.makeHTTPBinRequest()
  97. // When
  98. let encoded = try encoder.encode(["z": "z", "a": "a", "p": "p"], into: request)
  99. // Then
  100. let expected = """
  101. {"a":"a","p":"p","z":"z"}
  102. """
  103. XCTAssertEqual(encoded.httpBody?.asString, expected)
  104. }
  105. }
  106. final class URLEncodedFormParameterEncoderTests: BaseTestCase {
  107. func testThatQueryIsBodyEncodedAndProperContentTypeIsSetForPOSTRequest() throws {
  108. // Given
  109. let encoder = URLEncodedFormParameterEncoder()
  110. let request = URLRequest.makeHTTPBinRequest(method: .post)
  111. // When
  112. let newRequest = try encoder.encode(HTTPBinParameters.default, into: request)
  113. // Then
  114. XCTAssertEqual(newRequest.headers["Content-Type"], "application/x-www-form-urlencoded; charset=utf-8")
  115. XCTAssertEqual(newRequest.httpBody?.asString, "property=property")
  116. }
  117. func testThatQueryIsBodyEncodedButContentTypeIsNotSetWhenRequestAlreadyHasContentType() throws {
  118. // Given
  119. let encoder = URLEncodedFormParameterEncoder()
  120. var request = URLRequest.makeHTTPBinRequest(method: .post)
  121. request.headers.update(.contentType("type"))
  122. // When
  123. let newRequest = try encoder.encode(HTTPBinParameters.default, into: request)
  124. // Then
  125. XCTAssertEqual(newRequest.headers["Content-Type"], "type")
  126. XCTAssertEqual(newRequest.httpBody?.asString, "property=property")
  127. }
  128. func testThatEncoderCanBeCustomized() throws {
  129. // Given
  130. let urlEncoder = URLEncodedFormEncoder(boolEncoding: .literal)
  131. let encoder = URLEncodedFormParameterEncoder(encoder: urlEncoder)
  132. let request = URLRequest.makeHTTPBinRequest()
  133. // When
  134. let newRequest = try encoder.encode(["bool": true], into: request)
  135. // Then
  136. let components = URLComponents(url: newRequest.url!, resolvingAgainstBaseURL: false)
  137. XCTAssertEqual(components?.percentEncodedQuery, "bool=true")
  138. }
  139. func testThatQueryIsInURLWhenDestinationIsURLAndMethodIsPOST() throws {
  140. // Given
  141. let encoder = URLEncodedFormParameterEncoder(destination: .queryString)
  142. let request = URLRequest.makeHTTPBinRequest(method: .post)
  143. // When
  144. let newRequest = try encoder.encode(HTTPBinParameters.default, into: request)
  145. // Then
  146. let components = URLComponents(url: newRequest.url!, resolvingAgainstBaseURL: false)
  147. XCTAssertEqual(components?.percentEncodedQuery, "property=property")
  148. }
  149. func testThatQueryIsNilWhenEncodableResultsInAnEmptyString() throws {
  150. // Given
  151. let encoder = URLEncodedFormParameterEncoder(destination: .queryString)
  152. let request = URLRequest.makeHTTPBinRequest()
  153. // When
  154. let newRequest = try encoder.encode([String: String](), into: request)
  155. // Then
  156. let components = URLComponents(url: newRequest.url!, resolvingAgainstBaseURL: false)
  157. XCTAssertNil(components?.percentEncodedQuery)
  158. }
  159. }
  160. final class URLEncodedFormEncoderTests: BaseTestCase {
  161. func testEncoderThrowsErrorWhenAttemptingToEncodeNilInKeyedContainer() {
  162. // Given
  163. let encoder = URLEncodedFormEncoder()
  164. let parameters = FailingOptionalStruct(testedContainer: .keyed)
  165. // When
  166. let result = Result<String, Error> { try encoder.encode(parameters) }
  167. // Then
  168. XCTAssertTrue(result.isFailure)
  169. }
  170. func testEncoderThrowsErrorWhenAttemptingToEncodeNilInUnkeyedContainer() {
  171. // Given
  172. let encoder = URLEncodedFormEncoder()
  173. let parameters = FailingOptionalStruct(testedContainer: .unkeyed)
  174. // When
  175. let result = Result<String, Error> { try encoder.encode(parameters) }
  176. // Then
  177. XCTAssertTrue(result.isFailure)
  178. }
  179. func testEncoderCanEncodeDictionary() {
  180. // Given
  181. let encoder = URLEncodedFormEncoder()
  182. let parameters = ["a": "a"]
  183. // When
  184. let result = Result<String, Error> { try encoder.encode(parameters) }
  185. // Then
  186. XCTAssertEqual(result.success, "a=a")
  187. }
  188. func testEncoderCanEncodeDouble() {
  189. // Given
  190. let encoder = URLEncodedFormEncoder()
  191. let parameters = ["a": 1.0]
  192. // When
  193. let result = Result<String, Error> { try encoder.encode(parameters) }
  194. // Then
  195. XCTAssertEqual(result.success, "a=1.0")
  196. }
  197. func testEncoderCanEncodeFloat() {
  198. // Given
  199. let encoder = URLEncodedFormEncoder()
  200. let parameters: [String: Float] = ["a": 1.0]
  201. // When
  202. let result = Result<String, Error> { try encoder.encode(parameters) }
  203. // Then
  204. XCTAssertEqual(result.success, "a=1.0")
  205. }
  206. func testEncoderCanEncodeInt8() {
  207. // Given
  208. let encoder = URLEncodedFormEncoder()
  209. let parameters: [String: Int8] = ["a": 1]
  210. // When
  211. let result = Result<String, Error> { try encoder.encode(parameters) }
  212. // Then
  213. XCTAssertEqual(result.success, "a=1")
  214. }
  215. func testEncoderCanEncodeInt16() {
  216. // Given
  217. let encoder = URLEncodedFormEncoder()
  218. let parameters: [String: Int16] = ["a": 1]
  219. // When
  220. let result = Result<String, Error> { try encoder.encode(parameters) }
  221. // Then
  222. XCTAssertEqual(result.success, "a=1")
  223. }
  224. func testEncoderCanEncodeInt32() {
  225. // Given
  226. let encoder = URLEncodedFormEncoder()
  227. let parameters: [String: Int32] = ["a": 1]
  228. // When
  229. let result = Result<String, Error> { try encoder.encode(parameters) }
  230. // Then
  231. XCTAssertEqual(result.success, "a=1")
  232. }
  233. func testEncoderCanEncodeInt64() {
  234. // Given
  235. let encoder = URLEncodedFormEncoder()
  236. let parameters: [String: Int64] = ["a": 1]
  237. // When
  238. let result = Result<String, Error> { try encoder.encode(parameters) }
  239. // Then
  240. XCTAssertEqual(result.success, "a=1")
  241. }
  242. func testEncoderCanEncodeUInt() {
  243. // Given
  244. let encoder = URLEncodedFormEncoder()
  245. let parameters: [String: UInt] = ["a": 1]
  246. // When
  247. let result = Result<String, Error> { try encoder.encode(parameters) }
  248. // Then
  249. XCTAssertEqual(result.success, "a=1")
  250. }
  251. func testEncoderCanEncodeUInt8() {
  252. // Given
  253. let encoder = URLEncodedFormEncoder()
  254. let parameters: [String: UInt8] = ["a": 1]
  255. // When
  256. let result = Result<String, Error> { try encoder.encode(parameters) }
  257. // Then
  258. XCTAssertEqual(result.success, "a=1")
  259. }
  260. func testEncoderCanEncodeUInt16() {
  261. // Given
  262. let encoder = URLEncodedFormEncoder()
  263. let parameters: [String: UInt16] = ["a": 1]
  264. // When
  265. let result = Result<String, Error> { try encoder.encode(parameters) }
  266. // Then
  267. XCTAssertEqual(result.success, "a=1")
  268. }
  269. func testEncoderCanEncodeUInt32() {
  270. // Given
  271. let encoder = URLEncodedFormEncoder()
  272. let parameters: [String: UInt32] = ["a": 1]
  273. // When
  274. let result = Result<String, Error> { try encoder.encode(parameters) }
  275. // Then
  276. XCTAssertEqual(result.success, "a=1")
  277. }
  278. func testEncoderCanEncodeUInt64() {
  279. // Given
  280. let encoder = URLEncodedFormEncoder()
  281. let parameters: [String: UInt64] = ["a": 1]
  282. // When
  283. let result = Result<String, Error> { try encoder.encode(parameters) }
  284. // Then
  285. XCTAssertEqual(result.success, "a=1")
  286. }
  287. func testThatNestedDictionariesHaveBracketedKeys() {
  288. // Given
  289. let encoder = URLEncodedFormEncoder()
  290. let parameters = ["a": ["b": "b"]]
  291. // When
  292. let result = Result<String, Error> { try encoder.encode(parameters) }
  293. // Then
  294. XCTAssertEqual(result.success, "a%5Bb%5D=b")
  295. }
  296. func testThatEncodableStructCanBeEncoded() {
  297. // Given
  298. let encoder = URLEncodedFormEncoder()
  299. let parameters = EncodableStruct()
  300. // When
  301. let result = Result<String, Error> { try encoder.encode(parameters) }
  302. // Then
  303. let expected = "five%5Ba%5D=a&four%5B%5D=1&four%5B%5D=2&four%5B%5D=3&one=one&seven%5Ba%5D=a&six%5Ba%5D%5Bb%5D=b&three=1&two=2"
  304. XCTAssertEqual(result.success, expected)
  305. }
  306. func testThatManuallyEncodableStructCanBeEncoded() {
  307. // Given
  308. let encoder = URLEncodedFormEncoder()
  309. let parameters = ManuallyEncodableStruct()
  310. // When
  311. let result = Result<String, Error> { try encoder.encode(parameters) }
  312. // Then
  313. let expected = "root%5B%5D%5B%5D%5B%5D=1&root%5B%5D%5B%5D%5B%5D=2&root%5B%5D%5B%5D%5B%5D=3&root%5B%5D%5B%5D=1&root%5B%5D%5B%5D=2&root%5B%5D%5B%5D=3&root%5B%5D%5Ba%5D%5Bstring%5D=string"
  314. XCTAssertEqual(result.success, expected)
  315. }
  316. func testThatEncodableClassWithNoInheritanceCanBeEncoded() {
  317. // Given
  318. let encoder = URLEncodedFormEncoder()
  319. let parameters = EncodableSuperclass()
  320. // When
  321. let result = Result<String, Error> { try encoder.encode(parameters) }
  322. // Then
  323. XCTAssertEqual(result.success, "one=one&three=1&two=2")
  324. }
  325. func testThatEncodableSubclassCanBeEncoded() {
  326. // Given
  327. let encoder = URLEncodedFormEncoder()
  328. let parameters = EncodableSubclass()
  329. // When
  330. let result = Result<String, Error> { try encoder.encode(parameters) }
  331. // Then
  332. let expected = "five%5Ba%5D=a&five%5Bb%5D=b&four%5B%5D=1&four%5B%5D=2&four%5B%5D=3&one=one&three=1&two=2"
  333. XCTAssertEqual(result.success, expected)
  334. }
  335. func testThatEncodableSubclassCanBeEncodedInImplementationOrderWhenAlphabetizeKeysIsFalse() {
  336. // Given
  337. let encoder = URLEncodedFormEncoder(alphabetizeKeyValuePairs: false)
  338. let parameters = EncodableStruct()
  339. // When
  340. let result = Result<String, Error> { try encoder.encode(parameters) }
  341. // Then
  342. let expected = "one=one&two=2&three=1&four%5B%5D=1&four%5B%5D=2&four%5B%5D=3&five%5Ba%5D=a&six%5Ba%5D%5Bb%5D=b&seven%5Ba%5D=a"
  343. XCTAssertEqual(result.success, expected)
  344. }
  345. func testThatManuallyEncodableSubclassCanBeEncoded() {
  346. // Given
  347. let encoder = URLEncodedFormEncoder()
  348. let parameters = ManuallyEncodableSubclass()
  349. // When
  350. let result = Result<String, Error> { try encoder.encode(parameters) }
  351. // Then
  352. let expected = "five%5Ba%5D=a&five%5Bb%5D=b&four%5Bfive%5D=2&four%5Bfour%5D=one"
  353. XCTAssertEqual(result.success, expected)
  354. }
  355. func testThatARootArrayCannotBeEncoded() {
  356. // Given
  357. let encoder = URLEncodedFormEncoder()
  358. let parameters = [1]
  359. // When
  360. let result = Result<String, Error> { try encoder.encode(parameters) }
  361. // Then
  362. XCTAssertFalse(result.isSuccess)
  363. }
  364. func testThatARootValueCannotBeEncoded() {
  365. // Given
  366. let encoder = URLEncodedFormEncoder()
  367. let parameters = "string"
  368. // When
  369. let result = Result<String, Error> { try encoder.encode(parameters) }
  370. // Then
  371. XCTAssertFalse(result.isSuccess)
  372. }
  373. func testThatOptionalValuesCannotBeEncoded() {
  374. // Given
  375. let encoder = URLEncodedFormEncoder()
  376. let parameters: [String: String?] = ["string": nil]
  377. // When
  378. let result = Result<String, Error> { try encoder.encode(parameters) }
  379. // Then
  380. XCTAssertFalse(result.isSuccess)
  381. }
  382. func testThatArraysCanBeEncodedWithoutBrackets() {
  383. // Given
  384. let encoder = URLEncodedFormEncoder(arrayEncoding: .noBrackets)
  385. let parameters = ["array": [1, 2]]
  386. // When
  387. let result = Result<String, Error> { try encoder.encode(parameters) }
  388. // Then
  389. XCTAssertEqual(result.success, "array=1&array=2")
  390. }
  391. func testThatBoolsCanBeLiteralEncoded() {
  392. // Given
  393. let encoder = URLEncodedFormEncoder(boolEncoding: .literal)
  394. let parameters = ["bool": true]
  395. // When
  396. let result = Result<String, Error> { try encoder.encode(parameters) }
  397. // Then
  398. XCTAssertEqual(result.success, "bool=true")
  399. }
  400. func testThatDataCanBeEncoded() {
  401. // Given
  402. let encoder = URLEncodedFormEncoder()
  403. let parameters = ["data": Data("data".utf8)]
  404. // When
  405. let result = Result<String, Error> { try encoder.encode(parameters) }
  406. // Then
  407. XCTAssertEqual(result.success, "data=ZGF0YQ%3D%3D")
  408. }
  409. func testThatCustomDataEncodingFailsWhenErrorIsThrown() {
  410. // Given
  411. struct DataEncodingError: Error {}
  412. let encoder = URLEncodedFormEncoder(dataEncoding: .custom { _ in throw DataEncodingError() })
  413. let parameters = ["data": Data("data".utf8)]
  414. // When
  415. let result = Result<String, Error> { try encoder.encode(parameters) }
  416. // Then
  417. XCTAssertTrue(result.isFailure)
  418. XCTAssertTrue(result.failure is DataEncodingError)
  419. }
  420. func testThatDatesCanBeEncoded() {
  421. // Given
  422. let encoder = URLEncodedFormEncoder(dateEncoding: .deferredToDate)
  423. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  424. // When
  425. let result = Result<String, Error> { try encoder.encode(parameters) }
  426. // Then
  427. XCTAssertEqual(result.success, "date=123.456")
  428. }
  429. func testThatDatesCanBeEncodedAsSecondsSince1970() {
  430. // Given
  431. let encoder = URLEncodedFormEncoder(dateEncoding: .secondsSince1970)
  432. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  433. // When
  434. let result = Result<String, Error> { try encoder.encode(parameters) }
  435. // Then
  436. XCTAssertEqual(result.success, "date=978307323.456")
  437. }
  438. func testThatDatesCanBeEncodedAsMillisecondsSince1970() {
  439. // Given
  440. let encoder = URLEncodedFormEncoder(dateEncoding: .millisecondsSince1970)
  441. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  442. // When
  443. let result = Result<String, Error> { try encoder.encode(parameters) }
  444. // Then
  445. XCTAssertEqual(result.success, "date=978307323456.0")
  446. }
  447. func testThatDatesCanBeEncodedAsISO8601Formatted() {
  448. // Given
  449. let encoder = URLEncodedFormEncoder(dateEncoding: .iso8601)
  450. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  451. // When
  452. let result = Result<String, Error> { try encoder.encode(parameters) }
  453. // Then
  454. XCTAssertEqual(result.success, "date=2001-01-01T00%3A02%3A03Z")
  455. }
  456. func testThatDatesCanBeEncodedAsFormatted() {
  457. // Given
  458. let dateFormatter = DateFormatter()
  459. dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSS"
  460. dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
  461. let encoder = URLEncodedFormEncoder(dateEncoding: .formatted(dateFormatter))
  462. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  463. // When
  464. let result = Result<String, Error> { try encoder.encode(parameters) }
  465. // Then
  466. XCTAssertEqual(result.success, "date=2001-01-01%2000%3A02%3A03.4560")
  467. }
  468. func testThatDatesCanBeEncodedAsCustomFormatted() {
  469. // Given
  470. let encoder = URLEncodedFormEncoder(dateEncoding: .custom { "\($0.timeIntervalSinceReferenceDate)" })
  471. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  472. // When
  473. let result = Result<String, Error> { try encoder.encode(parameters) }
  474. // Then
  475. XCTAssertEqual(result.success, "date=123.456")
  476. }
  477. func testEncoderThrowsErrorWhenCustomDateEncodingFails() {
  478. // Given
  479. struct DateEncodingError: Error {}
  480. let encoder = URLEncodedFormEncoder(dateEncoding: .custom { _ in throw DateEncodingError() })
  481. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  482. // When
  483. let result = Result<String, Error> { try encoder.encode(parameters) }
  484. // Then
  485. XCTAssertTrue(result.isFailure)
  486. XCTAssertTrue(result.failure is DateEncodingError)
  487. }
  488. func testThatKeysCanBeEncodedIntoSnakeCase() {
  489. // Given
  490. let encoder = URLEncodedFormEncoder(keyEncoding: .convertToSnakeCase)
  491. let parameters = ["oneTwoThree": "oneTwoThree"]
  492. // When
  493. let result = Result<String, Error> { try encoder.encode(parameters) }
  494. // Then
  495. XCTAssertEqual(result.success, "one_two_three=oneTwoThree")
  496. }
  497. func testThatKeysCanBeEncodedIntoKebabCase() {
  498. // Given
  499. let encoder = URLEncodedFormEncoder(keyEncoding: .convertToKebabCase)
  500. let parameters = ["oneTwoThree": "oneTwoThree"]
  501. // When
  502. let result = Result<String, Error> { try encoder.encode(parameters) }
  503. // Then
  504. XCTAssertEqual(result.success, "one-two-three=oneTwoThree")
  505. }
  506. func testThatKeysCanBeEncodedIntoACapitalizedString() {
  507. // Given
  508. let encoder = URLEncodedFormEncoder(keyEncoding: .capitalized)
  509. let parameters = ["oneTwoThree": "oneTwoThree"]
  510. // When
  511. let result = Result<String, Error> { try encoder.encode(parameters) }
  512. // Then
  513. XCTAssertEqual(result.success, "OneTwoThree=oneTwoThree")
  514. }
  515. func testThatKeysCanBeEncodedIntoALowercasedString() {
  516. // Given
  517. let encoder = URLEncodedFormEncoder(keyEncoding: .lowercased)
  518. let parameters = ["oneTwoThree": "oneTwoThree"]
  519. // When
  520. let result = Result<String, Error> { try encoder.encode(parameters) }
  521. // Then
  522. XCTAssertEqual(result.success, "onetwothree=oneTwoThree")
  523. }
  524. func testThatKeysCanBeEncodedIntoAnUppercasedString() {
  525. // Given
  526. let encoder = URLEncodedFormEncoder(keyEncoding: .uppercased)
  527. let parameters = ["oneTwoThree": "oneTwoThree"]
  528. // When
  529. let result = Result<String, Error> { try encoder.encode(parameters) }
  530. // Then
  531. XCTAssertEqual(result.success, "ONETWOTHREE=oneTwoThree")
  532. }
  533. func testThatKeysCanBeCustomEncoded() {
  534. // Given
  535. let encoder = URLEncodedFormEncoder(keyEncoding: .custom { _ in "A" })
  536. let parameters = ["oneTwoThree": "oneTwoThree"]
  537. // When
  538. let result = Result<String, Error> { try encoder.encode(parameters) }
  539. // Then
  540. XCTAssertEqual(result.success, "A=oneTwoThree")
  541. }
  542. func testThatSpacesCanBeEncodedAsPluses() {
  543. // Given
  544. let encoder = URLEncodedFormEncoder(spaceEncoding: .plusReplaced)
  545. let parameters = ["spaces": "replace with spaces"]
  546. // When
  547. let result = Result<String, Error> { try encoder.encode(parameters) }
  548. // Then
  549. XCTAssertEqual(result.success, "spaces=replace+with+spaces")
  550. }
  551. func testThatEscapedCharactersCanBeCustomized() {
  552. // Given
  553. var allowed = CharacterSet.afURLQueryAllowed
  554. allowed.remove(charactersIn: "?/")
  555. let encoder = URLEncodedFormEncoder(allowedCharacters: allowed)
  556. let parameters = ["allowed": "?/"]
  557. // When
  558. let result = Result<String, Error> { try encoder.encode(parameters) }
  559. // Then
  560. XCTAssertEqual(result.success, "allowed=%3F%2F")
  561. }
  562. func testThatUnreservedCharactersAreNotPercentEscaped() {
  563. // Given
  564. let encoder = URLEncodedFormEncoder()
  565. let parameters = ["lowercase": "abcdefghijklmnopqrstuvwxyz",
  566. "numbers": "0123456789",
  567. "uppercase": "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
  568. // When
  569. let result = Result<String, Error> { try encoder.encode(parameters) }
  570. // Then
  571. let expected = "lowercase=abcdefghijklmnopqrstuvwxyz&numbers=0123456789&uppercase=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  572. XCTAssertEqual(result.success, expected)
  573. }
  574. func testThatReservedCharactersArePercentEscaped() {
  575. // Given
  576. let encoder = URLEncodedFormEncoder()
  577. let generalDelimiters = ":#[]@"
  578. let subDelimiters = "!$&'()*+,;="
  579. let parameters = ["reserved": "\(generalDelimiters)\(subDelimiters)"]
  580. // When
  581. let result = Result<String, Error> { try encoder.encode(parameters) }
  582. // Then
  583. XCTAssertEqual(result.success, "reserved=%3A%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D")
  584. }
  585. func testThatIllegalASCIICharactersArePercentEscaped() {
  586. // Given
  587. let encoder = URLEncodedFormEncoder()
  588. let parameters = ["illegal": " \"#%<>[]\\^`{}|"]
  589. // When
  590. let result = Result<String, Error> { try encoder.encode(parameters) }
  591. // Then
  592. XCTAssertEqual(result.success, "illegal=%20%22%23%25%3C%3E%5B%5D%5C%5E%60%7B%7D%7C")
  593. }
  594. func testThatAmpersandsInKeysAndValuesArePercentEscaped() {
  595. // Given
  596. let encoder = URLEncodedFormEncoder()
  597. let parameters = ["foo&bar": "baz&qux", "foobar": "bazqux"]
  598. // When
  599. let result = Result<String, Error> { try encoder.encode(parameters) }
  600. // Then
  601. XCTAssertEqual(result.success, "foo%26bar=baz%26qux&foobar=bazqux")
  602. }
  603. func testThatQuestionMarksInKeysAndValuesAreNotPercentEscaped() {
  604. // Given
  605. let encoder = URLEncodedFormEncoder()
  606. let parameters = ["?foo?": "?bar?"]
  607. // When
  608. let result = Result<String, Error> { try encoder.encode(parameters) }
  609. // Then
  610. XCTAssertEqual(result.success, "?foo?=?bar?")
  611. }
  612. func testThatSlashesInKeysAndValuesAreNotPercentEscaped() {
  613. // Given
  614. let encoder = URLEncodedFormEncoder()
  615. let parameters = ["foo": "/bar/baz/qux"]
  616. // When
  617. let result = Result<String, Error> { try encoder.encode(parameters) }
  618. // Then
  619. XCTAssertEqual(result.success, "foo=/bar/baz/qux")
  620. }
  621. func testThatSpacesInKeysAndValuesArePercentEscaped() {
  622. // Given
  623. let encoder = URLEncodedFormEncoder()
  624. let parameters = [" foo ": " bar "]
  625. // When
  626. let result = Result<String, Error> { try encoder.encode(parameters) }
  627. // Then
  628. XCTAssertEqual(result.success, "%20foo%20=%20bar%20")
  629. }
  630. func testThatPlusesInKeysAndValuesArePercentEscaped() {
  631. // Given
  632. let encoder = URLEncodedFormEncoder()
  633. let parameters = ["+foo+": "+bar+"]
  634. // When
  635. let result = Result<String, Error> { try encoder.encode(parameters) }
  636. // Then
  637. XCTAssertEqual(result.success, "%2Bfoo%2B=%2Bbar%2B")
  638. }
  639. func testThatPercentsInKeysAndValuesArePercentEscaped() {
  640. // Given
  641. let encoder = URLEncodedFormEncoder()
  642. let parameters = ["percent%": "%25"]
  643. // When
  644. let result = Result<String, Error> { try encoder.encode(parameters) }
  645. // Then
  646. XCTAssertEqual(result.success, "percent%25=%2525")
  647. }
  648. func testThatNonLatinCharactersArePercentEscaped() {
  649. // Given
  650. let encoder = URLEncodedFormEncoder()
  651. let parameters = ["french": "français",
  652. "japanese": "日本語",
  653. "arabic": "العربية",
  654. "emoji": "😃"]
  655. // When
  656. let result = Result<String, Error> { try encoder.encode(parameters) }
  657. // Then
  658. let expectedParameterValues = ["arabic=%D8%A7%D9%84%D8%B9%D8%B1%D8%A8%D9%8A%D8%A9",
  659. "emoji=%F0%9F%98%83",
  660. "french=fran%C3%A7ais",
  661. "japanese=%E6%97%A5%E6%9C%AC%E8%AA%9E"].joined(separator: "&")
  662. XCTAssertEqual(result.success, expectedParameterValues)
  663. }
  664. func testStringWithThousandsOfChineseCharactersIsPercentEscaped() {
  665. // Given
  666. let encoder = URLEncodedFormEncoder()
  667. let repeatedCount = 2000
  668. let parameters = ["chinese": String(repeating: "一二三四五六七八九十", count: repeatedCount)]
  669. // When
  670. let result = Result<String, Error> { try encoder.encode(parameters) }
  671. // Then
  672. let escaped = String(repeating: "%E4%B8%80%E4%BA%8C%E4%B8%89%E5%9B%9B%E4%BA%94%E5%85%AD%E4%B8%83%E5%85%AB%E4%B9%9D%E5%8D%81",
  673. count: repeatedCount)
  674. let expected = "chinese=\(escaped)"
  675. XCTAssertEqual(result.success, expected)
  676. }
  677. }
  678. private struct EncodableStruct: Encodable {
  679. let one = "one"
  680. let two = 2
  681. let three = true
  682. let four = [1, 2, 3]
  683. let five = ["a": "a"]
  684. let six = ["a": ["b": "b"]]
  685. let seven = NestedEncodableStruct()
  686. }
  687. private struct NestedEncodableStruct: Encodable {
  688. let a = "a"
  689. }
  690. private class EncodableSuperclass: Encodable {
  691. let one = "one"
  692. let two = 2
  693. let three = true
  694. }
  695. private final class EncodableSubclass: EncodableSuperclass {
  696. let four = [1, 2, 3]
  697. let five = ["a": "a", "b": "b"]
  698. private enum CodingKeys: String, CodingKey {
  699. case four, five
  700. }
  701. override func encode(to encoder: Encoder) throws {
  702. try super.encode(to: encoder)
  703. var container = encoder.container(keyedBy: CodingKeys.self)
  704. try container.encode(four, forKey: .four)
  705. try container.encode(five, forKey: .five)
  706. }
  707. }
  708. private final class ManuallyEncodableSubclass: EncodableSuperclass {
  709. let four = [1, 2, 3]
  710. let five = ["a": "a", "b": "b"]
  711. private enum CodingKeys: String, CodingKey {
  712. case four, five
  713. }
  714. override func encode(to encoder: Encoder) throws {
  715. var keyedContainer = encoder.container(keyedBy: CodingKeys.self)
  716. try keyedContainer.encode(four, forKey: .four)
  717. try keyedContainer.encode(five, forKey: .five)
  718. let superEncoder = keyedContainer.superEncoder()
  719. var superContainer = superEncoder.container(keyedBy: CodingKeys.self)
  720. try superContainer.encode(one, forKey: .four)
  721. let keyedSuperEncoder = keyedContainer.superEncoder(forKey: .four)
  722. var superKeyedContainer = keyedSuperEncoder.container(keyedBy: CodingKeys.self)
  723. try superKeyedContainer.encode(two, forKey: .five)
  724. var unkeyedContainer = keyedContainer.nestedUnkeyedContainer(forKey: .four)
  725. let unkeyedSuperEncoder = unkeyedContainer.superEncoder()
  726. var keyedUnkeyedSuperContainer = unkeyedSuperEncoder.container(keyedBy: CodingKeys.self)
  727. try keyedUnkeyedSuperContainer.encode(one, forKey: .four)
  728. }
  729. }
  730. private struct ManuallyEncodableStruct: Encodable {
  731. let a = ["string": "string"]
  732. let b = [1, 2, 3]
  733. private enum RootKey: String, CodingKey {
  734. case root
  735. }
  736. private enum TypeKeys: String, CodingKey {
  737. case a, b
  738. }
  739. func encode(to encoder: Encoder) throws {
  740. var container = encoder.container(keyedBy: RootKey.self)
  741. var nestedKeyedContainer = container.nestedContainer(keyedBy: TypeKeys.self, forKey: .root)
  742. try nestedKeyedContainer.encode(a, forKey: .a)
  743. var nestedUnkeyedContainer = container.nestedUnkeyedContainer(forKey: .root)
  744. try nestedUnkeyedContainer.encode(b)
  745. var nestedUnkeyedKeyedContainer = nestedUnkeyedContainer.nestedContainer(keyedBy: TypeKeys.self)
  746. try nestedUnkeyedKeyedContainer.encode(a, forKey: .a)
  747. var nestedUnkeyedUnkeyedContainer = nestedUnkeyedContainer.nestedUnkeyedContainer()
  748. try nestedUnkeyedUnkeyedContainer.encode(b)
  749. }
  750. }
  751. private struct FailingOptionalStruct: Encodable {
  752. enum TestedContainer {
  753. case keyed, unkeyed
  754. }
  755. enum CodingKeys: String, CodingKey { case a }
  756. let testedContainer: TestedContainer
  757. func encode(to encoder: Encoder) throws {
  758. var container = encoder.container(keyedBy: CodingKeys.self)
  759. switch testedContainer {
  760. case .keyed:
  761. var nested = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .a)
  762. try nested.encodeNil(forKey: .a)
  763. case .unkeyed:
  764. var nested = container.nestedUnkeyedContainer(forKey: .a)
  765. try nested.encodeNil()
  766. }
  767. }
  768. }