ParameterEncoderTests.swift 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248
  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 = Endpoint().urlRequest
  31. // When
  32. let newRequest = try encoder.encode(TestParameters.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 = Endpoint().urlRequest
  41. request.headers.update(.contentType("type"))
  42. // When
  43. let newRequest = try encoder.encode(TestParameters.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 = Endpoint().urlRequest
  54. // When
  55. let newRequest = try encoder.encode(TestParameters.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 = Endpoint().urlRequest
  68. // When
  69. let encoded = try encoder.encode(TestParameters.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 = Endpoint().urlRequest
  80. // When
  81. let encoded = try encoder.encode(TestParameters.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 = Endpoint().urlRequest
  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 = Endpoint(method: .post).urlRequest
  111. // When
  112. let newRequest = try encoder.encode(TestParameters.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 = Endpoint(method: .post).urlRequest
  121. request.headers.update(.contentType("type"))
  122. // When
  123. let newRequest = try encoder.encode(TestParameters.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 = Endpoint().urlRequest
  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 = Endpoint(method: .post).urlRequest
  143. // When
  144. let newRequest = try encoder.encode(TestParameters.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 = Endpoint().urlRequest
  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 testEncoderCanEncodeDictionary() {
  162. // Given
  163. let encoder = URLEncodedFormEncoder()
  164. let parameters = ["a": "a"]
  165. // When
  166. let result = Result<String, any Error> { try encoder.encode(parameters) }
  167. // Then
  168. XCTAssertEqual(result.success, "a=a")
  169. }
  170. func testEncoderCanEncodeDecimal() {
  171. // Given
  172. let encoder = URLEncodedFormEncoder()
  173. let decimal: Decimal = 1.0
  174. let parameters = ["a": decimal]
  175. // When
  176. let result = Result<String, any Error> { try encoder.encode(parameters) }
  177. // Then
  178. XCTAssertEqual(result.success, "a=1")
  179. }
  180. func testEncoderCanEncodeDecimalWithHighPrecision() {
  181. // Given
  182. let encoder = URLEncodedFormEncoder()
  183. let decimal: Decimal = 1.123456
  184. let parameters = ["a": decimal]
  185. // When
  186. let result = Result<String, any Error> { try encoder.encode(parameters) }
  187. // Then
  188. XCTAssertEqual(result.success, "a=1.123456")
  189. }
  190. func testEncoderCanEncodeDouble() {
  191. // Given
  192. let encoder = URLEncodedFormEncoder()
  193. let parameters = ["a": 1.0]
  194. // When
  195. let result = Result<String, any Error> { try encoder.encode(parameters) }
  196. // Then
  197. XCTAssertEqual(result.success, "a=1.0")
  198. }
  199. func testEncoderCanEncodeFloat() {
  200. // Given
  201. let encoder = URLEncodedFormEncoder()
  202. let parameters: [String: Float] = ["a": 1.0]
  203. // When
  204. let result = Result<String, any Error> { try encoder.encode(parameters) }
  205. // Then
  206. XCTAssertEqual(result.success, "a=1.0")
  207. }
  208. func testEncoderCanEncodeInt8() {
  209. // Given
  210. let encoder = URLEncodedFormEncoder()
  211. let parameters: [String: Int8] = ["a": 1]
  212. // When
  213. let result = Result<String, any Error> { try encoder.encode(parameters) }
  214. // Then
  215. XCTAssertEqual(result.success, "a=1")
  216. }
  217. func testEncoderCanEncodeInt16() {
  218. // Given
  219. let encoder = URLEncodedFormEncoder()
  220. let parameters: [String: Int16] = ["a": 1]
  221. // When
  222. let result = Result<String, any Error> { try encoder.encode(parameters) }
  223. // Then
  224. XCTAssertEqual(result.success, "a=1")
  225. }
  226. func testEncoderCanEncodeInt32() {
  227. // Given
  228. let encoder = URLEncodedFormEncoder()
  229. let parameters: [String: Int32] = ["a": 1]
  230. // When
  231. let result = Result<String, any Error> { try encoder.encode(parameters) }
  232. // Then
  233. XCTAssertEqual(result.success, "a=1")
  234. }
  235. func testEncoderCanEncodeInt64() {
  236. // Given
  237. let encoder = URLEncodedFormEncoder()
  238. let parameters: [String: Int64] = ["a": 1]
  239. // When
  240. let result = Result<String, any Error> { try encoder.encode(parameters) }
  241. // Then
  242. XCTAssertEqual(result.success, "a=1")
  243. }
  244. func testEncoderCanEncodeUInt() {
  245. // Given
  246. let encoder = URLEncodedFormEncoder()
  247. let parameters: [String: UInt] = ["a": 1]
  248. // When
  249. let result = Result<String, any Error> { try encoder.encode(parameters) }
  250. // Then
  251. XCTAssertEqual(result.success, "a=1")
  252. }
  253. func testEncoderCanEncodeUInt8() {
  254. // Given
  255. let encoder = URLEncodedFormEncoder()
  256. let parameters: [String: UInt8] = ["a": 1]
  257. // When
  258. let result = Result<String, any Error> { try encoder.encode(parameters) }
  259. // Then
  260. XCTAssertEqual(result.success, "a=1")
  261. }
  262. func testEncoderCanEncodeUInt16() {
  263. // Given
  264. let encoder = URLEncodedFormEncoder()
  265. let parameters: [String: UInt16] = ["a": 1]
  266. // When
  267. let result = Result<String, any Error> { try encoder.encode(parameters) }
  268. // Then
  269. XCTAssertEqual(result.success, "a=1")
  270. }
  271. func testEncoderCanEncodeUInt32() {
  272. // Given
  273. let encoder = URLEncodedFormEncoder()
  274. let parameters: [String: UInt32] = ["a": 1]
  275. // When
  276. let result = Result<String, any Error> { try encoder.encode(parameters) }
  277. // Then
  278. XCTAssertEqual(result.success, "a=1")
  279. }
  280. func testEncoderCanEncodeUInt64() {
  281. // Given
  282. let encoder = URLEncodedFormEncoder()
  283. let parameters: [String: UInt64] = ["a": 1]
  284. // When
  285. let result = Result<String, any Error> { try encoder.encode(parameters) }
  286. // Then
  287. XCTAssertEqual(result.success, "a=1")
  288. }
  289. func testThatNestedDictionariesCanHaveBracketKeyPathsByDefault() {
  290. // Given
  291. let encoder = URLEncodedFormEncoder()
  292. let parameters = ["a": ["b": "b"]]
  293. // When
  294. let result = Result<String, any Error> { try encoder.encode(parameters) }
  295. // Then
  296. XCTAssertEqual(result.success, "a%5Bb%5D=b")
  297. }
  298. func testThatNestedDictionariesCanHaveExplicitBracketKeyPaths() {
  299. // Given
  300. let encoder = URLEncodedFormEncoder(keyPathEncoding: .brackets)
  301. let parameters = ["a": ["b": "b"]]
  302. // When
  303. let result = Result<String, any Error> { try encoder.encode(parameters) }
  304. // Then
  305. XCTAssertEqual(result.success, "a%5Bb%5D=b")
  306. }
  307. func testThatNestedDictionariesCanHaveDottedKeyPaths() {
  308. // Given
  309. let encoder = URLEncodedFormEncoder(keyPathEncoding: .dots)
  310. let parameters = ["a": ["b": "b"]]
  311. // When
  312. let result = Result<String, any Error> { try encoder.encode(parameters) }
  313. // Then
  314. XCTAssertEqual(result.success, "a.b=b")
  315. }
  316. func testThatNestedDictionariesCanHaveCustomKeyPaths() {
  317. // Given
  318. let encoder = URLEncodedFormEncoder(keyPathEncoding: .init { "-\($0)" })
  319. let parameters = ["a": ["b": "b"]]
  320. // When
  321. let result = Result<String, any Error> { try encoder.encode(parameters) }
  322. // Then
  323. XCTAssertEqual(result.success, "a-b=b")
  324. }
  325. func testThatEncodableStructCanBeEncoded() {
  326. // Given
  327. let encoder = URLEncodedFormEncoder()
  328. let parameters = EncodableStruct()
  329. // When
  330. let result = Result<String, any Error> { try encoder.encode(parameters) }
  331. // Then
  332. 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"
  333. XCTAssertEqual(result.success, expected)
  334. }
  335. func testThatManuallyEncodableStructCanBeEncoded() {
  336. // Given
  337. let encoder = URLEncodedFormEncoder()
  338. let parameters = ManuallyEncodableStruct()
  339. // When
  340. let result = Result<String, any Error> { try encoder.encode(parameters) }
  341. // Then
  342. 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"
  343. XCTAssertEqual(result.success, expected)
  344. }
  345. func testThatEncodableClassWithNoInheritanceCanBeEncoded() {
  346. // Given
  347. let encoder = URLEncodedFormEncoder()
  348. let parameters = EncodableSuperclass()
  349. // When
  350. let result = Result<String, any Error> { try encoder.encode(parameters) }
  351. // Then
  352. XCTAssertEqual(result.success, "one=one&three=1&two=2")
  353. }
  354. func testThatEncodableSubclassCanBeEncoded() {
  355. // Given
  356. let encoder = URLEncodedFormEncoder()
  357. let parameters = EncodableSubclass()
  358. // When
  359. let result = Result<String, any Error> { try encoder.encode(parameters) }
  360. // Then
  361. 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"
  362. XCTAssertEqual(result.success, expected)
  363. }
  364. func testThatEncodableSubclassCanBeEncodedInImplementationOrderWhenAlphabetizeKeysIsFalse() {
  365. // Given
  366. let encoder = URLEncodedFormEncoder(alphabetizeKeyValuePairs: false)
  367. let parameters = EncodableStruct()
  368. // When
  369. let result = Result<String, any Error> { try encoder.encode(parameters) }
  370. // Then
  371. 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"
  372. XCTAssertEqual(result.success, expected)
  373. }
  374. func testThatManuallyEncodableSubclassCanBeEncoded() {
  375. // Given
  376. let encoder = URLEncodedFormEncoder()
  377. let parameters = ManuallyEncodableSubclass()
  378. // When
  379. let result = Result<String, any Error> { try encoder.encode(parameters) }
  380. // Then
  381. let expected = "five%5Ba%5D=a&five%5Bb%5D=b&four%5Bfive%5D=2&four%5Bfour%5D=one"
  382. XCTAssertEqual(result.success, expected)
  383. }
  384. func testThatARootArrayCannotBeEncoded() {
  385. // Given
  386. let encoder = URLEncodedFormEncoder()
  387. let parameters = [1]
  388. // When
  389. let result = Result<String, any Error> { try encoder.encode(parameters) }
  390. // Then
  391. XCTAssertFalse(result.isSuccess)
  392. }
  393. func testThatARootValueCannotBeEncoded() {
  394. // Given
  395. let encoder = URLEncodedFormEncoder()
  396. let parameters = "string"
  397. // When
  398. let result = Result<String, any Error> { try encoder.encode(parameters) }
  399. // Then
  400. XCTAssertFalse(result.isSuccess)
  401. }
  402. func testThatEncodableSuperclassCanBeEncodedWithIndexInBrackets() {
  403. // Given
  404. let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
  405. let parameters = ["foo": [EncodableSuperclass()]]
  406. // When
  407. let result = Result<String, any Error> { try encoder.encode(parameters) }
  408. // Then
  409. XCTAssertEqual(result.success, "foo%5B0%5D%5Bone%5D=one&foo%5B0%5D%5Bthree%5D=1&foo%5B0%5D%5Btwo%5D=2")
  410. }
  411. func testThatEncodableSubclassCanBeEncodedWithIndexInBrackets() {
  412. // Given
  413. let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
  414. let parameters = EncodableSubclass()
  415. // When
  416. let result = Result<String, any Error> { try encoder.encode(parameters) }
  417. // Then
  418. let expected = "five%5Ba%5D=a&five%5Bb%5D=b&four%5B0%5D=1&four%5B1%5D=2&four%5B2%5D=3&one=one&three=1&two=2"
  419. XCTAssertEqual(result.success, expected)
  420. }
  421. func testThatManuallyEncodableSubclassCanBeEncodedWithIndexInBrackets() {
  422. // Given
  423. let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
  424. let parameters = ManuallyEncodableSubclass()
  425. // When
  426. let result = Result<String, any Error> { try encoder.encode(parameters) }
  427. // Then
  428. let expected = "five%5Ba%5D=a&five%5Bb%5D=b&four%5Bfive%5D=2&four%5Bfour%5D=one"
  429. XCTAssertEqual(result.success, expected)
  430. }
  431. func testThatEncodableStructCanBeEncodedWithIndexInBrackets() {
  432. // Given
  433. let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
  434. let parameters = EncodableStruct()
  435. // When
  436. let result = Result<String, any Error> { try encoder.encode(parameters) }
  437. // Then
  438. let expected = "five%5Ba%5D=a&four%5B0%5D=1&four%5B1%5D=2&four%5B2%5D=3&one=one&seven%5Ba%5D=a&six%5Ba%5D%5Bb%5D=b&three=1&two=2"
  439. XCTAssertEqual(result.success, expected)
  440. }
  441. func testThatManuallyEncodableStructCanBeEncodedWithIndexInBrackets() {
  442. // Given
  443. let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
  444. let parameters = ManuallyEncodableStruct()
  445. // When
  446. let result = Result<String, any Error> { try encoder.encode(parameters) }
  447. // then
  448. let expected = "root%5B0%5D%5B0%5D=1&root%5B0%5D%5B1%5D=2&root%5B0%5D%5B2%5D=3&root%5B1%5D%5Ba%5D%5Bstring%5D=string&root%5B2%5D%5B0%5D%5B0%5D=1&root%5B2%5D%5B0%5D%5B1%5D=2&root%5B2%5D%5B0%5D%5B2%5D=3"
  449. XCTAssertEqual(result.success, expected)
  450. }
  451. func testThatArrayNestedDictionaryIntValueCanBeEncodedWithIndexInBrackets() {
  452. // Given
  453. let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
  454. let parameters = ["foo": [["bar": 2], ["qux": 3], ["quy": 4]]]
  455. // When
  456. let result = Result<String, any Error> { try encoder.encode(parameters) }
  457. // Then
  458. XCTAssertEqual(result.success, "foo%5B0%5D%5Bbar%5D=2&foo%5B1%5D%5Bqux%5D=3&foo%5B2%5D%5Bquy%5D=4")
  459. }
  460. func testThatArrayNestedDictionaryStringValueCanBeEncodedWithIndexInBrackets() {
  461. // Given
  462. let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
  463. let parameters = ["foo": [["bar": "2"], ["qux": "3"], ["quy": "4"]]]
  464. // When
  465. let result = Result<String, any Error> { try encoder.encode(parameters) }
  466. // Then
  467. XCTAssertEqual(result.success, "foo%5B0%5D%5Bbar%5D=2&foo%5B1%5D%5Bqux%5D=3&foo%5B2%5D%5Bquy%5D=4")
  468. }
  469. func testThatArrayNestedDictionaryBoolValueCanBeEncodedWithIndexInBrackets() {
  470. // Given
  471. let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
  472. let parameters = ["foo": [["bar": true], ["qux": false], ["quy": true]]]
  473. // When
  474. let result = Result<String, any Error> { try encoder.encode(parameters) }
  475. // Then
  476. XCTAssertEqual(result.success, "foo%5B0%5D%5Bbar%5D=1&foo%5B1%5D%5Bqux%5D=0&foo%5B2%5D%5Bquy%5D=1")
  477. }
  478. func testThatArraysCanBeEncodedWithoutBrackets() {
  479. // Given
  480. let encoder = URLEncodedFormEncoder(arrayEncoding: .noBrackets)
  481. let parameters = ["array": [1, 2]]
  482. // When
  483. let result = Result<String, any Error> { try encoder.encode(parameters) }
  484. // Then
  485. XCTAssertEqual(result.success, "array=1&array=2")
  486. }
  487. func testThatArraysCanBeEncodedWithCustomClosure() {
  488. // Given
  489. let encoder = URLEncodedFormEncoder(arrayEncoding: .custom { key, index in
  490. "\(key).\(index + 1)"
  491. })
  492. let parameters = ["array": [1, 2]]
  493. // When
  494. let result = Result<String, any Error> { try encoder.encode(parameters) }
  495. // Then
  496. XCTAssertEqual(result.success, "array.1=1&array.2=2")
  497. }
  498. func testThatBoolsCanBeLiteralEncoded() {
  499. // Given
  500. let encoder = URLEncodedFormEncoder(boolEncoding: .literal)
  501. let parameters = ["bool": true]
  502. // When
  503. let result = Result<String, any Error> { try encoder.encode(parameters) }
  504. // Then
  505. XCTAssertEqual(result.success, "bool=true")
  506. }
  507. func testThatDataCanBeEncoded() {
  508. // Given
  509. let encoder = URLEncodedFormEncoder()
  510. let parameters = ["data": Data("data".utf8)]
  511. // When
  512. let result = Result<String, any Error> { try encoder.encode(parameters) }
  513. // Then
  514. XCTAssertEqual(result.success, "data=ZGF0YQ%3D%3D")
  515. }
  516. func testThatCustomDataEncodingFailsWhenErrorIsThrown() {
  517. // Given
  518. struct DataEncodingError: Error {}
  519. let encoder = URLEncodedFormEncoder(dataEncoding: .custom { _ in throw DataEncodingError() })
  520. let parameters = ["data": Data("data".utf8)]
  521. // When
  522. let result = Result<String, any Error> { try encoder.encode(parameters) }
  523. // Then
  524. XCTAssertTrue(result.isFailure)
  525. XCTAssertTrue(result.failure is DataEncodingError)
  526. }
  527. func testThatDatesCanBeEncoded() {
  528. // Given
  529. let encoder = URLEncodedFormEncoder(dateEncoding: .deferredToDate)
  530. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  531. // When
  532. let result = Result<String, any Error> { try encoder.encode(parameters) }
  533. // Then
  534. XCTAssertEqual(result.success, "date=123.456")
  535. }
  536. func testThatDatesCanBeEncodedAsSecondsSince1970() {
  537. // Given
  538. let encoder = URLEncodedFormEncoder(dateEncoding: .secondsSince1970)
  539. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  540. // When
  541. let result = Result<String, any Error> { try encoder.encode(parameters) }
  542. // Then
  543. XCTAssertEqual(result.success, "date=978307323.456")
  544. }
  545. func testThatDatesCanBeEncodedAsMillisecondsSince1970() {
  546. // Given
  547. let encoder = URLEncodedFormEncoder(dateEncoding: .millisecondsSince1970)
  548. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  549. // When
  550. let result = Result<String, any Error> { try encoder.encode(parameters) }
  551. // Then
  552. XCTAssertEqual(result.success, "date=978307323456.0")
  553. }
  554. func testThatDatesCanBeEncodedAsISO8601Formatted() {
  555. // Given
  556. let encoder = URLEncodedFormEncoder(dateEncoding: .iso8601)
  557. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  558. // When
  559. let result = Result<String, any Error> { try encoder.encode(parameters) }
  560. // Then
  561. XCTAssertEqual(result.success, "date=2001-01-01T00%3A02%3A03Z")
  562. }
  563. func testThatDatesCanBeEncodedAsFormatted() {
  564. // Given
  565. let dateFormatter = DateFormatter()
  566. dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSS"
  567. dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
  568. dateFormatter.calendar = Calendar(identifier: .gregorian)
  569. let encoder = URLEncodedFormEncoder(dateEncoding: .formatted(dateFormatter))
  570. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  571. // When
  572. let result = Result<String, any Error> { try encoder.encode(parameters) }
  573. // Then
  574. XCTAssertEqual(result.success, "date=2001-01-01%2000%3A02%3A03.4560")
  575. }
  576. func testThatDatesCanBeEncodedAsCustomFormatted() {
  577. // Given
  578. let encoder = URLEncodedFormEncoder(dateEncoding: .custom { "\($0.timeIntervalSinceReferenceDate)" })
  579. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  580. // When
  581. let result = Result<String, any Error> { try encoder.encode(parameters) }
  582. // Then
  583. XCTAssertEqual(result.success, "date=123.456")
  584. }
  585. func testEncoderThrowsErrorWhenCustomDateEncodingFails() {
  586. // Given
  587. struct DateEncodingError: Error {}
  588. let encoder = URLEncodedFormEncoder(dateEncoding: .custom { _ in throw DateEncodingError() })
  589. let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
  590. // When
  591. let result = Result<String, any Error> { try encoder.encode(parameters) }
  592. // Then
  593. XCTAssertTrue(result.isFailure)
  594. XCTAssertTrue(result.failure is DateEncodingError)
  595. }
  596. func testThatKeysCanBeEncodedIntoSnakeCase() {
  597. // Given
  598. let encoder = URLEncodedFormEncoder(keyEncoding: .convertToSnakeCase)
  599. let parameters = ["oneTwoThree": "oneTwoThree"]
  600. // When
  601. let result = Result<String, any Error> { try encoder.encode(parameters) }
  602. // Then
  603. XCTAssertEqual(result.success, "one_two_three=oneTwoThree")
  604. }
  605. func testThatKeysCanBeEncodedIntoKebabCase() {
  606. // Given
  607. let encoder = URLEncodedFormEncoder(keyEncoding: .convertToKebabCase)
  608. let parameters = ["oneTwoThree": "oneTwoThree"]
  609. // When
  610. let result = Result<String, any Error> { try encoder.encode(parameters) }
  611. // Then
  612. XCTAssertEqual(result.success, "one-two-three=oneTwoThree")
  613. }
  614. func testThatKeysCanBeEncodedIntoACapitalizedString() {
  615. // Given
  616. let encoder = URLEncodedFormEncoder(keyEncoding: .capitalized)
  617. let parameters = ["oneTwoThree": "oneTwoThree"]
  618. // When
  619. let result = Result<String, any Error> { try encoder.encode(parameters) }
  620. // Then
  621. XCTAssertEqual(result.success, "OneTwoThree=oneTwoThree")
  622. }
  623. func testThatKeysCanBeEncodedIntoALowercasedString() {
  624. // Given
  625. let encoder = URLEncodedFormEncoder(keyEncoding: .lowercased)
  626. let parameters = ["oneTwoThree": "oneTwoThree"]
  627. // When
  628. let result = Result<String, any Error> { try encoder.encode(parameters) }
  629. // Then
  630. XCTAssertEqual(result.success, "onetwothree=oneTwoThree")
  631. }
  632. func testThatKeysCanBeEncodedIntoAnUppercasedString() {
  633. // Given
  634. let encoder = URLEncodedFormEncoder(keyEncoding: .uppercased)
  635. let parameters = ["oneTwoThree": "oneTwoThree"]
  636. // When
  637. let result = Result<String, any Error> { try encoder.encode(parameters) }
  638. // Then
  639. XCTAssertEqual(result.success, "ONETWOTHREE=oneTwoThree")
  640. }
  641. func testThatKeysCanBeCustomEncoded() {
  642. // Given
  643. let encoder = URLEncodedFormEncoder(keyEncoding: .custom { _ in "A" })
  644. let parameters = ["oneTwoThree": "oneTwoThree"]
  645. // When
  646. let result = Result<String, any Error> { try encoder.encode(parameters) }
  647. // Then
  648. XCTAssertEqual(result.success, "A=oneTwoThree")
  649. }
  650. func testThatNilCanBeEncodedByDroppingTheKeyByDefault() {
  651. // Given
  652. let encoder = URLEncodedFormEncoder()
  653. let parameters: [String: String?] = ["a": nil]
  654. // When
  655. let result = Result<String, any Error> { try encoder.encode(parameters) }
  656. // Then
  657. XCTAssertEqual(result.success, "")
  658. }
  659. func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheKeyByDefault() {
  660. // Given
  661. let encoder = URLEncodedFormEncoder()
  662. // When
  663. let result = Result<String, any Error> { try encoder.encode(OptionalEncodableStruct()) }
  664. // Then
  665. XCTAssertEqual(result.success, "one=one")
  666. }
  667. func testThatNilCanBeEncodedAsNull() {
  668. // Given
  669. let encoder = URLEncodedFormEncoder(nilEncoding: .null)
  670. let parameters: [String: String?] = ["a": nil]
  671. // When
  672. let result = Result<String, any Error> { try encoder.encode(parameters) }
  673. // Then
  674. XCTAssertEqual(result.success, "a=null")
  675. }
  676. func testThatNilCanBeEncodedInSynthesizedEncodableAsNull() {
  677. // Given
  678. let encoder = URLEncodedFormEncoder(nilEncoding: .null)
  679. // When
  680. let result = Result<String, any Error> { try encoder.encode(OptionalEncodableStruct()) }
  681. // Then
  682. XCTAssertEqual(result.success, "one=one&two=null")
  683. }
  684. func testThatNilCanBeEncodedByDroppingTheKey() {
  685. // Given
  686. let encoder = URLEncodedFormEncoder(nilEncoding: .dropKey)
  687. let parameters: [String: String?] = ["a": nil]
  688. // When
  689. let result = Result<String, any Error> { try encoder.encode(parameters) }
  690. // Then
  691. XCTAssertEqual(result.success, "")
  692. }
  693. func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheKey() {
  694. // Given
  695. let encoder = URLEncodedFormEncoder(nilEncoding: .dropKey)
  696. // When
  697. let result = Result<String, any Error> { try encoder.encode(OptionalEncodableStruct()) }
  698. // Then
  699. XCTAssertEqual(result.success, "one=one")
  700. }
  701. func testThatNilCanBeEncodedByDroppingTheValue() {
  702. // Given
  703. let encoder = URLEncodedFormEncoder(nilEncoding: .dropValue)
  704. let parameters: [String: String?] = ["a": nil]
  705. // When
  706. let result = Result<String, any Error> { try encoder.encode(parameters) }
  707. // Then
  708. XCTAssertEqual(result.success, "a=")
  709. }
  710. func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheValue() {
  711. // Given
  712. let encoder = URLEncodedFormEncoder(nilEncoding: .dropValue)
  713. // When
  714. let result = Result<String, any Error> { try encoder.encode(OptionalEncodableStruct()) }
  715. // Then
  716. XCTAssertEqual(result.success, "one=one&two=")
  717. }
  718. func testThatSpacesCanBeEncodedAsPluses() {
  719. // Given
  720. let encoder = URLEncodedFormEncoder(spaceEncoding: .plusReplaced)
  721. let parameters = ["spaces": "replace with spaces"]
  722. // When
  723. let result = Result<String, any Error> { try encoder.encode(parameters) }
  724. // Then
  725. XCTAssertEqual(result.success, "spaces=replace+with+spaces")
  726. }
  727. func testThatEscapedCharactersCanBeCustomized() {
  728. // Given
  729. var allowed = CharacterSet.afURLQueryAllowed
  730. allowed.remove(charactersIn: "?/")
  731. let encoder = URLEncodedFormEncoder(allowedCharacters: allowed)
  732. let parameters = ["allowed": "?/"]
  733. // When
  734. let result = Result<String, any Error> { try encoder.encode(parameters) }
  735. // Then
  736. XCTAssertEqual(result.success, "allowed=%3F%2F")
  737. }
  738. func testThatUnreservedCharactersAreNotPercentEscaped() {
  739. // Given
  740. let encoder = URLEncodedFormEncoder()
  741. let parameters = ["lowercase": "abcdefghijklmnopqrstuvwxyz",
  742. "numbers": "0123456789",
  743. "uppercase": "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
  744. // When
  745. let result = Result<String, any Error> { try encoder.encode(parameters) }
  746. // Then
  747. let expected = "lowercase=abcdefghijklmnopqrstuvwxyz&numbers=0123456789&uppercase=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  748. XCTAssertEqual(result.success, expected)
  749. }
  750. func testThatReservedCharactersArePercentEscaped() {
  751. // Given
  752. let encoder = URLEncodedFormEncoder()
  753. let generalDelimiters = ":#[]@"
  754. let subDelimiters = "!$&'()*+,;="
  755. let parameters = ["reserved": "\(generalDelimiters)\(subDelimiters)"]
  756. // When
  757. let result = Result<String, any Error> { try encoder.encode(parameters) }
  758. // Then
  759. XCTAssertEqual(result.success, "reserved=%3A%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D")
  760. }
  761. func testThatIllegalASCIICharactersArePercentEscaped() {
  762. // Given
  763. let encoder = URLEncodedFormEncoder()
  764. let parameters = ["illegal": " \"#%<>[]\\^`{}|"]
  765. // When
  766. let result = Result<String, any Error> { try encoder.encode(parameters) }
  767. // Then
  768. XCTAssertEqual(result.success, "illegal=%20%22%23%25%3C%3E%5B%5D%5C%5E%60%7B%7D%7C")
  769. }
  770. func testThatAmpersandsInKeysAndValuesArePercentEscaped() {
  771. // Given
  772. let encoder = URLEncodedFormEncoder()
  773. let parameters = ["foo&bar": "baz&qux", "foobar": "bazqux"]
  774. // When
  775. let result = Result<String, any Error> { try encoder.encode(parameters) }
  776. // Then
  777. XCTAssertEqual(result.success, "foo%26bar=baz%26qux&foobar=bazqux")
  778. }
  779. func testThatQuestionMarksInKeysAndValuesAreNotPercentEscaped() {
  780. // Given
  781. let encoder = URLEncodedFormEncoder()
  782. let parameters = ["?foo?": "?bar?"]
  783. // When
  784. let result = Result<String, any Error> { try encoder.encode(parameters) }
  785. // Then
  786. XCTAssertEqual(result.success, "?foo?=?bar?")
  787. }
  788. func testThatSlashesInKeysAndValuesAreNotPercentEscaped() {
  789. // Given
  790. let encoder = URLEncodedFormEncoder()
  791. let parameters = ["foo": "/bar/baz/qux"]
  792. // When
  793. let result = Result<String, any Error> { try encoder.encode(parameters) }
  794. // Then
  795. XCTAssertEqual(result.success, "foo=/bar/baz/qux")
  796. }
  797. func testThatSpacesInKeysAndValuesArePercentEscaped() {
  798. // Given
  799. let encoder = URLEncodedFormEncoder()
  800. let parameters = [" foo ": " bar "]
  801. // When
  802. let result = Result<String, any Error> { try encoder.encode(parameters) }
  803. // Then
  804. XCTAssertEqual(result.success, "%20foo%20=%20bar%20")
  805. }
  806. func testThatPlusesInKeysAndValuesArePercentEscaped() {
  807. // Given
  808. let encoder = URLEncodedFormEncoder()
  809. let parameters = ["+foo+": "+bar+"]
  810. // When
  811. let result = Result<String, any Error> { try encoder.encode(parameters) }
  812. // Then
  813. XCTAssertEqual(result.success, "%2Bfoo%2B=%2Bbar%2B")
  814. }
  815. func testThatPercentsInKeysAndValuesArePercentEscaped() {
  816. // Given
  817. let encoder = URLEncodedFormEncoder()
  818. let parameters = ["percent%": "%25"]
  819. // When
  820. let result = Result<String, any Error> { try encoder.encode(parameters) }
  821. // Then
  822. XCTAssertEqual(result.success, "percent%25=%2525")
  823. }
  824. func testThatNonLatinCharactersArePercentEscaped() {
  825. // Given
  826. let encoder = URLEncodedFormEncoder()
  827. let parameters = ["french": "français",
  828. "japanese": "日本語",
  829. "arabic": "العربية",
  830. "emoji": "😃"]
  831. // When
  832. let result = Result<String, any Error> { try encoder.encode(parameters) }
  833. // Then
  834. let expectedParameterValues = ["arabic=%D8%A7%D9%84%D8%B9%D8%B1%D8%A8%D9%8A%D8%A9",
  835. "emoji=%F0%9F%98%83",
  836. "french=fran%C3%A7ais",
  837. "japanese=%E6%97%A5%E6%9C%AC%E8%AA%9E"].joined(separator: "&")
  838. XCTAssertEqual(result.success, expectedParameterValues)
  839. }
  840. func testStringWithThousandsOfChineseCharactersIsPercentEscaped() {
  841. // Given
  842. let encoder = URLEncodedFormEncoder()
  843. let repeatedCount = 2000
  844. let parameters = ["chinese": String(repeating: "一二三四五六七八九十", count: repeatedCount)]
  845. // When
  846. let result = Result<String, any Error> { try encoder.encode(parameters) }
  847. // Then
  848. 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",
  849. count: repeatedCount)
  850. let expected = "chinese=\(escaped)"
  851. XCTAssertEqual(result.success, expected)
  852. }
  853. }
  854. final class StaticParameterEncoderInstanceTests: BaseTestCase {
  855. func takeParameterEncoder(_ parameterEncoder: any ParameterEncoder) {
  856. _ = parameterEncoder
  857. }
  858. func testThatJSONParameterEncoderCanBeCreatedStaticallyFromProtocol() {
  859. // Given, When, Then
  860. takeParameterEncoder(.json())
  861. }
  862. func testThatURLEncodedFormParameterEncoderCanBeCreatedStaticallyFromProtocol() {
  863. // Given, When, Then
  864. takeParameterEncoder(.urlEncodedForm())
  865. }
  866. }
  867. private struct EncodableStruct: Encodable {
  868. let one = "one"
  869. let two = 2
  870. let three = true
  871. let four = [1, 2, 3]
  872. let five = ["a": "a"]
  873. let six = ["a": ["b": "b"]]
  874. let seven = NestedEncodableStruct()
  875. }
  876. private struct NestedEncodableStruct: Encodable {
  877. let a = "a"
  878. }
  879. private struct OptionalEncodableStruct: Encodable {
  880. let one = "one"
  881. let two: String? = nil
  882. }
  883. private class EncodableSuperclass: Encodable {
  884. let one = "one"
  885. let two = 2
  886. let three = true
  887. }
  888. private final class EncodableSubclass: EncodableSuperclass {
  889. let four = [1, 2, 3]
  890. let five = ["a": "a", "b": "b"]
  891. private enum CodingKeys: String, CodingKey {
  892. case four, five
  893. }
  894. override func encode(to encoder: any Encoder) throws {
  895. try super.encode(to: encoder)
  896. var container = encoder.container(keyedBy: CodingKeys.self)
  897. try container.encode(four, forKey: .four)
  898. try container.encode(five, forKey: .five)
  899. }
  900. }
  901. private final class ManuallyEncodableSubclass: EncodableSuperclass {
  902. let four = [1, 2, 3]
  903. let five = ["a": "a", "b": "b"]
  904. private enum CodingKeys: String, CodingKey {
  905. case four, five
  906. }
  907. override func encode(to encoder: any Encoder) throws {
  908. var keyedContainer = encoder.container(keyedBy: CodingKeys.self)
  909. try keyedContainer.encode(four, forKey: .four)
  910. try keyedContainer.encode(five, forKey: .five)
  911. let superEncoder = keyedContainer.superEncoder()
  912. var superContainer = superEncoder.container(keyedBy: CodingKeys.self)
  913. try superContainer.encode(one, forKey: .four)
  914. let keyedSuperEncoder = keyedContainer.superEncoder(forKey: .four)
  915. var superKeyedContainer = keyedSuperEncoder.container(keyedBy: CodingKeys.self)
  916. try superKeyedContainer.encode(two, forKey: .five)
  917. var unkeyedContainer = keyedContainer.nestedUnkeyedContainer(forKey: .four)
  918. let unkeyedSuperEncoder = unkeyedContainer.superEncoder()
  919. var keyedUnkeyedSuperContainer = unkeyedSuperEncoder.container(keyedBy: CodingKeys.self)
  920. try keyedUnkeyedSuperContainer.encode(one, forKey: .four)
  921. }
  922. }
  923. private struct ManuallyEncodableStruct: Encodable {
  924. let a = ["string": "string"]
  925. let b = [1, 2, 3]
  926. private enum RootKey: String, CodingKey {
  927. case root
  928. }
  929. private enum TypeKeys: String, CodingKey {
  930. case a, b
  931. }
  932. func encode(to encoder: any Encoder) throws {
  933. var container = encoder.container(keyedBy: RootKey.self)
  934. var nestedKeyedContainer = container.nestedContainer(keyedBy: TypeKeys.self, forKey: .root)
  935. try nestedKeyedContainer.encode(a, forKey: .a)
  936. var nestedUnkeyedContainer = container.nestedUnkeyedContainer(forKey: .root)
  937. try nestedUnkeyedContainer.encode(b)
  938. var nestedUnkeyedKeyedContainer = nestedUnkeyedContainer.nestedContainer(keyedBy: TypeKeys.self)
  939. try nestedUnkeyedKeyedContainer.encode(a, forKey: .a)
  940. var nestedUnkeyedUnkeyedContainer = nestedUnkeyedContainer.nestedUnkeyedContainer()
  941. try nestedUnkeyedUnkeyedContainer.encode(b)
  942. }
  943. }
  944. private struct FailingOptionalStruct: Encodable {
  945. enum TestedContainer {
  946. case keyed, unkeyed
  947. }
  948. enum CodingKeys: String, CodingKey { case a }
  949. let testedContainer: TestedContainer
  950. func encode(to encoder: any Encoder) throws {
  951. var container = encoder.container(keyedBy: CodingKeys.self)
  952. switch testedContainer {
  953. case .keyed:
  954. var nested = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .a)
  955. try nested.encodeNil(forKey: .a)
  956. case .unkeyed:
  957. var nested = container.nestedUnkeyedContainer(forKey: .a)
  958. try nested.encodeNil()
  959. }
  960. }
  961. }