import Alamofire
import XCTest
final class JSONParameterEncoderTests: BaseTestCase {
func testThatDataIsProperlyEncodedAndProperContentTypeIsSet() throws {
let encoder = JSONParameterEncoder()
let request = Endpoint().urlRequest
let newRequest = try encoder.encode(TestParameters.default, into: request)
XCTAssertEqual(newRequest.headers["Content-Type"], "application/json")
XCTAssertEqual(newRequest.httpBody?.asString, "{\"property\":\"property\"}")
}
func testThatDataIsProperlyEncodedButContentTypeIsNotSetIfRequestAlreadyHasAContentType() throws {
let encoder = JSONParameterEncoder()
var request = Endpoint().urlRequest
request.headers.update(.contentType("type"))
let newRequest = try encoder.encode(TestParameters.default, into: request)
XCTAssertEqual(newRequest.headers["Content-Type"], "type")
XCTAssertEqual(newRequest.httpBody?.asString, "{\"property\":\"property\"}")
}
func testThatJSONEncoderCanBeCustomized() throws {
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let encoder = JSONParameterEncoder(encoder: jsonEncoder)
let request = Endpoint().urlRequest
let newRequest = try encoder.encode(TestParameters.default, into: request)
let expected = """
{
"property" : "property"
}
"""
XCTAssertEqual(newRequest.httpBody?.asString, expected)
}
func testThatJSONEncoderDefaultWorks() throws {
let encoder = JSONParameterEncoder.default
let request = Endpoint().urlRequest
let encoded = try encoder.encode(TestParameters.default, into: request)
let expected = """
{"property":"property"}
"""
XCTAssertEqual(encoded.httpBody?.asString, expected)
}
func testThatJSONEncoderPrettyPrintedPrintsPretty() throws {
let encoder = JSONParameterEncoder.prettyPrinted
let request = Endpoint().urlRequest
let encoded = try encoder.encode(TestParameters.default, into: request)
let expected = """
{
"property" : "property"
}
"""
XCTAssertEqual(encoded.httpBody?.asString, expected)
}
}
final class SortedKeysJSONParameterEncoderTests: BaseTestCase {
func testTestJSONEncoderSortedKeysHasSortedKeys() throws {
guard #available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) else { return }
let encoder = JSONParameterEncoder.sortedKeys
let request = Endpoint().urlRequest
let encoded = try encoder.encode(["z": "z", "a": "a", "p": "p"], into: request)
let expected = """
{"a":"a","p":"p","z":"z"}
"""
XCTAssertEqual(encoded.httpBody?.asString, expected)
}
}
final class URLEncodedFormParameterEncoderTests: BaseTestCase {
func testThatQueryIsBodyEncodedAndProperContentTypeIsSetForPOSTRequest() throws {
let encoder = URLEncodedFormParameterEncoder()
let request = Endpoint(method: .post).urlRequest
let newRequest = try encoder.encode(TestParameters.default, into: request)
XCTAssertEqual(newRequest.headers["Content-Type"], "application/x-www-form-urlencoded; charset=utf-8")
XCTAssertEqual(newRequest.httpBody?.asString, "property=property")
}
func testThatQueryIsBodyEncodedButContentTypeIsNotSetWhenRequestAlreadyHasContentType() throws {
let encoder = URLEncodedFormParameterEncoder()
var request = Endpoint(method: .post).urlRequest
request.headers.update(.contentType("type"))
let newRequest = try encoder.encode(TestParameters.default, into: request)
XCTAssertEqual(newRequest.headers["Content-Type"], "type")
XCTAssertEqual(newRequest.httpBody?.asString, "property=property")
}
func testThatEncoderCanBeCustomized() throws {
let urlEncoder = URLEncodedFormEncoder(boolEncoding: .literal)
let encoder = URLEncodedFormParameterEncoder(encoder: urlEncoder)
let request = Endpoint().urlRequest
let newRequest = try encoder.encode(["bool": true], into: request)
let components = URLComponents(url: newRequest.url!, resolvingAgainstBaseURL: false)
XCTAssertEqual(components?.percentEncodedQuery, "bool=true")
}
func testThatQueryIsInURLWhenDestinationIsURLAndMethodIsPOST() throws {
let encoder = URLEncodedFormParameterEncoder(destination: .queryString)
let request = Endpoint(method: .post).urlRequest
let newRequest = try encoder.encode(TestParameters.default, into: request)
let components = URLComponents(url: newRequest.url!, resolvingAgainstBaseURL: false)
XCTAssertEqual(components?.percentEncodedQuery, "property=property")
}
func testThatQueryIsNilWhenEncodableResultsInAnEmptyString() throws {
let encoder = URLEncodedFormParameterEncoder(destination: .queryString)
let request = Endpoint().urlRequest
let newRequest = try encoder.encode([String: String](), into: request)
let components = URLComponents(url: newRequest.url!, resolvingAgainstBaseURL: false)
XCTAssertNil(components?.percentEncodedQuery)
}
}
final class URLEncodedFormEncoderTests: BaseTestCase {
func testEncoderCanEncodeDictionary() {
let encoder = URLEncodedFormEncoder()
let parameters = ["a": "a"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=a")
}
func testEncoderCanEncodeDecimal() {
let encoder = URLEncodedFormEncoder()
let decimal: Decimal = 1.0
let parameters = ["a": decimal]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeDecimalWithHighPrecision() {
let encoder = URLEncodedFormEncoder()
let decimal: Decimal = 1.123456
let parameters = ["a": decimal]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1.123456")
}
func testEncoderCanEncodeDouble() {
let encoder = URLEncodedFormEncoder()
let parameters = ["a": 1.0]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1.0")
}
func testEncoderCanEncodeFloat() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: Float] = ["a": 1.0]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1.0")
}
func testEncoderCanEncodeInt8() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: Int8] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeInt16() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: Int16] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeInt32() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: Int32] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeInt64() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: Int64] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeUInt() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: UInt] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeUInt8() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: UInt8] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeUInt16() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: UInt16] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeUInt32() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: UInt32] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testEncoderCanEncodeUInt64() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: UInt64] = ["a": 1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=1")
}
func testThatNestedDictionariesCanHaveBracketKeyPathsByDefault() {
let encoder = URLEncodedFormEncoder()
let parameters = ["a": ["b": "b"]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a%5Bb%5D=b")
}
func testThatNestedDictionariesCanHaveExplicitBracketKeyPaths() {
let encoder = URLEncodedFormEncoder(keyPathEncoding: .brackets)
let parameters = ["a": ["b": "b"]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a%5Bb%5D=b")
}
func testThatNestedDictionariesCanHaveDottedKeyPaths() {
let encoder = URLEncodedFormEncoder(keyPathEncoding: .dots)
let parameters = ["a": ["b": "b"]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a.b=b")
}
func testThatNestedDictionariesCanHaveCustomKeyPaths() {
let encoder = URLEncodedFormEncoder(keyPathEncoding: .init { "-\($0)" })
let parameters = ["a": ["b": "b"]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a-b=b")
}
func testThatEncodableStructCanBeEncoded() {
let encoder = URLEncodedFormEncoder()
let parameters = EncodableStruct()
let result = Result<String, any Error> { try encoder.encode(parameters) }
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"
XCTAssertEqual(result.success, expected)
}
func testThatManuallyEncodableStructCanBeEncoded() {
let encoder = URLEncodedFormEncoder()
let parameters = ManuallyEncodableStruct()
let result = Result<String, any Error> { try encoder.encode(parameters) }
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"
XCTAssertEqual(result.success, expected)
}
func testThatEncodableClassWithNoInheritanceCanBeEncoded() {
let encoder = URLEncodedFormEncoder()
let parameters = EncodableSuperclass()
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "one=one&three=1&two=2")
}
func testThatEncodableSubclassCanBeEncoded() {
let encoder = URLEncodedFormEncoder()
let parameters = EncodableSubclass()
let result = Result<String, any Error> { try encoder.encode(parameters) }
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"
XCTAssertEqual(result.success, expected)
}
func testThatEncodableSubclassCanBeEncodedInImplementationOrderWhenAlphabetizeKeysIsFalse() {
let encoder = URLEncodedFormEncoder(alphabetizeKeyValuePairs: false)
let parameters = EncodableStruct()
let result = Result<String, any Error> { try encoder.encode(parameters) }
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"
XCTAssertEqual(result.success, expected)
}
func testThatManuallyEncodableSubclassCanBeEncoded() {
let encoder = URLEncodedFormEncoder()
let parameters = ManuallyEncodableSubclass()
let result = Result<String, any Error> { try encoder.encode(parameters) }
let expected = "five%5Ba%5D=a&five%5Bb%5D=b&four%5Bfive%5D=2&four%5Bfour%5D=one"
XCTAssertEqual(result.success, expected)
}
func testThatARootArrayCannotBeEncoded() {
let encoder = URLEncodedFormEncoder()
let parameters = [1]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertFalse(result.isSuccess)
}
func testThatARootValueCannotBeEncoded() {
let encoder = URLEncodedFormEncoder()
let parameters = "string"
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertFalse(result.isSuccess)
}
func testThatEncodableSuperclassCanBeEncodedWithIndexInBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
let parameters = ["foo": [EncodableSuperclass()]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "foo%5B0%5D%5Bone%5D=one&foo%5B0%5D%5Bthree%5D=1&foo%5B0%5D%5Btwo%5D=2")
}
func testThatEncodableSubclassCanBeEncodedWithIndexInBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
let parameters = EncodableSubclass()
let result = Result<String, any Error> { try encoder.encode(parameters) }
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"
XCTAssertEqual(result.success, expected)
}
func testThatManuallyEncodableSubclassCanBeEncodedWithIndexInBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
let parameters = ManuallyEncodableSubclass()
let result = Result<String, any Error> { try encoder.encode(parameters) }
let expected = "five%5Ba%5D=a&five%5Bb%5D=b&four%5Bfive%5D=2&four%5Bfour%5D=one"
XCTAssertEqual(result.success, expected)
}
func testThatEncodableStructCanBeEncodedWithIndexInBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
let parameters = EncodableStruct()
let result = Result<String, any Error> { try encoder.encode(parameters) }
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"
XCTAssertEqual(result.success, expected)
}
func testThatManuallyEncodableStructCanBeEncodedWithIndexInBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
let parameters = ManuallyEncodableStruct()
let result = Result<String, any Error> { try encoder.encode(parameters) }
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"
XCTAssertEqual(result.success, expected)
}
func testThatArrayNestedDictionaryIntValueCanBeEncodedWithIndexInBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
let parameters = ["foo": [["bar": 2], ["qux": 3], ["quy": 4]]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "foo%5B0%5D%5Bbar%5D=2&foo%5B1%5D%5Bqux%5D=3&foo%5B2%5D%5Bquy%5D=4")
}
func testThatArrayNestedDictionaryStringValueCanBeEncodedWithIndexInBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
let parameters = ["foo": [["bar": "2"], ["qux": "3"], ["quy": "4"]]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "foo%5B0%5D%5Bbar%5D=2&foo%5B1%5D%5Bqux%5D=3&foo%5B2%5D%5Bquy%5D=4")
}
func testThatArrayNestedDictionaryBoolValueCanBeEncodedWithIndexInBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .indexInBrackets)
let parameters = ["foo": [["bar": true], ["qux": false], ["quy": true]]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "foo%5B0%5D%5Bbar%5D=1&foo%5B1%5D%5Bqux%5D=0&foo%5B2%5D%5Bquy%5D=1")
}
func testThatArraysCanBeEncodedWithoutBrackets() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .noBrackets)
let parameters = ["array": [1, 2]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "array=1&array=2")
}
func testThatArraysCanBeEncodedWithCustomClosure() {
let encoder = URLEncodedFormEncoder(arrayEncoding: .custom { key, index in
"\(key).\(index + 1)"
})
let parameters = ["array": [1, 2]]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "array.1=1&array.2=2")
}
func testThatBoolsCanBeLiteralEncoded() {
let encoder = URLEncodedFormEncoder(boolEncoding: .literal)
let parameters = ["bool": true]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "bool=true")
}
func testThatDataCanBeEncoded() {
let encoder = URLEncodedFormEncoder()
let parameters = ["data": Data("data".utf8)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "data=ZGF0YQ%3D%3D")
}
func testThatCustomDataEncodingFailsWhenErrorIsThrown() {
struct DataEncodingError: Error {}
let encoder = URLEncodedFormEncoder(dataEncoding: .custom { _ in throw DataEncodingError() })
let parameters = ["data": Data("data".utf8)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertTrue(result.isFailure)
XCTAssertTrue(result.failure is DataEncodingError)
}
func testThatDatesCanBeEncoded() {
let encoder = URLEncodedFormEncoder(dateEncoding: .deferredToDate)
let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "date=123.456")
}
func testThatDatesCanBeEncodedAsSecondsSince1970() {
let encoder = URLEncodedFormEncoder(dateEncoding: .secondsSince1970)
let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "date=978307323.456")
}
func testThatDatesCanBeEncodedAsMillisecondsSince1970() {
let encoder = URLEncodedFormEncoder(dateEncoding: .millisecondsSince1970)
let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "date=978307323456.0")
}
func testThatDatesCanBeEncodedAsISO8601Formatted() {
let encoder = URLEncodedFormEncoder(dateEncoding: .iso8601)
let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "date=2001-01-01T00%3A02%3A03Z")
}
func testThatDatesCanBeEncodedAsFormatted() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSS"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
dateFormatter.calendar = Calendar(identifier: .gregorian)
let encoder = URLEncodedFormEncoder(dateEncoding: .formatted(dateFormatter))
let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "date=2001-01-01%2000%3A02%3A03.4560")
}
func testThatDatesCanBeEncodedAsCustomFormatted() {
let encoder = URLEncodedFormEncoder(dateEncoding: .custom { "\($0.timeIntervalSinceReferenceDate)" })
let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "date=123.456")
}
func testEncoderThrowsErrorWhenCustomDateEncodingFails() {
struct DateEncodingError: Error {}
let encoder = URLEncodedFormEncoder(dateEncoding: .custom { _ in throw DateEncodingError() })
let parameters = ["date": Date(timeIntervalSinceReferenceDate: 123.456)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertTrue(result.isFailure)
XCTAssertTrue(result.failure is DateEncodingError)
}
func testThatKeysCanBeEncodedIntoSnakeCase() {
let encoder = URLEncodedFormEncoder(keyEncoding: .convertToSnakeCase)
let parameters = ["oneTwoThree": "oneTwoThree"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "one_two_three=oneTwoThree")
}
func testThatKeysCanBeEncodedIntoKebabCase() {
let encoder = URLEncodedFormEncoder(keyEncoding: .convertToKebabCase)
let parameters = ["oneTwoThree": "oneTwoThree"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "one-two-three=oneTwoThree")
}
func testThatKeysCanBeEncodedIntoACapitalizedString() {
let encoder = URLEncodedFormEncoder(keyEncoding: .capitalized)
let parameters = ["oneTwoThree": "oneTwoThree"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "OneTwoThree=oneTwoThree")
}
func testThatKeysCanBeEncodedIntoALowercasedString() {
let encoder = URLEncodedFormEncoder(keyEncoding: .lowercased)
let parameters = ["oneTwoThree": "oneTwoThree"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "onetwothree=oneTwoThree")
}
func testThatKeysCanBeEncodedIntoAnUppercasedString() {
let encoder = URLEncodedFormEncoder(keyEncoding: .uppercased)
let parameters = ["oneTwoThree": "oneTwoThree"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "ONETWOTHREE=oneTwoThree")
}
func testThatKeysCanBeCustomEncoded() {
let encoder = URLEncodedFormEncoder(keyEncoding: .custom { _ in "A" })
let parameters = ["oneTwoThree": "oneTwoThree"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "A=oneTwoThree")
}
func testThatNilCanBeEncodedByDroppingTheKeyByDefault() {
let encoder = URLEncodedFormEncoder()
let parameters: [String: String?] = ["a": nil]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "")
}
func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheKeyByDefault() {
let encoder = URLEncodedFormEncoder()
let result = Result<String, any Error> { try encoder.encode(OptionalEncodableStruct()) }
XCTAssertEqual(result.success, "one=one")
}
func testThatNilCanBeEncodedAsNull() {
let encoder = URLEncodedFormEncoder(nilEncoding: .null)
let parameters: [String: String?] = ["a": nil]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=null")
}
func testThatNilCanBeEncodedInSynthesizedEncodableAsNull() {
let encoder = URLEncodedFormEncoder(nilEncoding: .null)
let result = Result<String, any Error> { try encoder.encode(OptionalEncodableStruct()) }
XCTAssertEqual(result.success, "one=one&two=null")
}
func testThatNilCanBeEncodedByDroppingTheKey() {
let encoder = URLEncodedFormEncoder(nilEncoding: .dropKey)
let parameters: [String: String?] = ["a": nil]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "")
}
func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheKey() {
let encoder = URLEncodedFormEncoder(nilEncoding: .dropKey)
let result = Result<String, any Error> { try encoder.encode(OptionalEncodableStruct()) }
XCTAssertEqual(result.success, "one=one")
}
func testThatNilCanBeEncodedByDroppingTheValue() {
let encoder = URLEncodedFormEncoder(nilEncoding: .dropValue)
let parameters: [String: String?] = ["a": nil]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "a=")
}
func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheValue() {
let encoder = URLEncodedFormEncoder(nilEncoding: .dropValue)
let result = Result<String, any Error> { try encoder.encode(OptionalEncodableStruct()) }
XCTAssertEqual(result.success, "one=one&two=")
}
func testThatSpacesCanBeEncodedAsPluses() {
let encoder = URLEncodedFormEncoder(spaceEncoding: .plusReplaced)
let parameters = ["spaces": "replace with spaces"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "spaces=replace+with+spaces")
}
func testThatEscapedCharactersCanBeCustomized() {
var allowed = CharacterSet.afURLQueryAllowed
allowed.remove(charactersIn: "?/")
let encoder = URLEncodedFormEncoder(allowedCharacters: allowed)
let parameters = ["allowed": "?/"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "allowed=%3F%2F")
}
func testThatUnreservedCharactersAreNotPercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = ["lowercase": "abcdefghijklmnopqrstuvwxyz",
"numbers": "0123456789",
"uppercase": "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
let expected = "lowercase=abcdefghijklmnopqrstuvwxyz&numbers=0123456789&uppercase=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
XCTAssertEqual(result.success, expected)
}
func testThatReservedCharactersArePercentEscaped() {
let encoder = URLEncodedFormEncoder()
let generalDelimiters = ":#[]@"
let subDelimiters = "!$&'()*+,;="
let parameters = ["reserved": "\(generalDelimiters)\(subDelimiters)"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "reserved=%3A%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D")
}
func testThatIllegalASCIICharactersArePercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = ["illegal": " \"#%<>[]\\^`{}|"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "illegal=%20%22%23%25%3C%3E%5B%5D%5C%5E%60%7B%7D%7C")
}
func testThatAmpersandsInKeysAndValuesArePercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = ["foo&bar": "baz&qux", "foobar": "bazqux"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "foo%26bar=baz%26qux&foobar=bazqux")
}
func testThatQuestionMarksInKeysAndValuesAreNotPercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = ["?foo?": "?bar?"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "?foo?=?bar?")
}
func testThatSlashesInKeysAndValuesAreNotPercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = ["foo": "/bar/baz/qux"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "foo=/bar/baz/qux")
}
func testThatSpacesInKeysAndValuesArePercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = [" foo ": " bar "]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "%20foo%20=%20bar%20")
}
func testThatPlusesInKeysAndValuesArePercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = ["+foo+": "+bar+"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "%2Bfoo%2B=%2Bbar%2B")
}
func testThatPercentsInKeysAndValuesArePercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = ["percent%": "%25"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
XCTAssertEqual(result.success, "percent%25=%2525")
}
func testThatNonLatinCharactersArePercentEscaped() {
let encoder = URLEncodedFormEncoder()
let parameters = ["french": "français",
"japanese": "日本語",
"arabic": "العربية",
"emoji": "😃"]
let result = Result<String, any Error> { try encoder.encode(parameters) }
let expectedParameterValues = ["arabic=%D8%A7%D9%84%D8%B9%D8%B1%D8%A8%D9%8A%D8%A9",
"emoji=%F0%9F%98%83",
"french=fran%C3%A7ais",
"japanese=%E6%97%A5%E6%9C%AC%E8%AA%9E"].joined(separator: "&")
XCTAssertEqual(result.success, expectedParameterValues)
}
func testStringWithThousandsOfChineseCharactersIsPercentEscaped() {
let encoder = URLEncodedFormEncoder()
let repeatedCount = 2000
let parameters = ["chinese": String(repeating: "一二三四五六七八九十", count: repeatedCount)]
let result = Result<String, any Error> { try encoder.encode(parameters) }
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",
count: repeatedCount)
let expected = "chinese=\(escaped)"
XCTAssertEqual(result.success, expected)
}
}
final class StaticParameterEncoderInstanceTests: BaseTestCase {
func takeParameterEncoder(_ parameterEncoder: any ParameterEncoder) {
_ = parameterEncoder
}
func testThatJSONParameterEncoderCanBeCreatedStaticallyFromProtocol() {
takeParameterEncoder(.json())
}
func testThatURLEncodedFormParameterEncoderCanBeCreatedStaticallyFromProtocol() {
takeParameterEncoder(.urlEncodedForm())
}
}
private struct EncodableStruct: Encodable {
let one = "one"
let two = 2
let three = true
let four = [1, 2, 3]
let five = ["a": "a"]
let six = ["a": ["b": "b"]]
let seven = NestedEncodableStruct()
}
private struct NestedEncodableStruct: Encodable {
let a = "a"
}
private struct OptionalEncodableStruct: Encodable {
let one = "one"
let two: String? = nil
}
private class EncodableSuperclass: Encodable {
let one = "one"
let two = 2
let three = true
}
private final class EncodableSubclass: EncodableSuperclass {
let four = [1, 2, 3]
let five = ["a": "a", "b": "b"]
private enum CodingKeys: String, CodingKey {
case four, five
}
override func encode(to encoder: any Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(four, forKey: .four)
try container.encode(five, forKey: .five)
}
}
private final class ManuallyEncodableSubclass: EncodableSuperclass {
let four = [1, 2, 3]
let five = ["a": "a", "b": "b"]
private enum CodingKeys: String, CodingKey {
case four, five
}
override func encode(to encoder: any Encoder) throws {
var keyedContainer = encoder.container(keyedBy: CodingKeys.self)
try keyedContainer.encode(four, forKey: .four)
try keyedContainer.encode(five, forKey: .five)
let superEncoder = keyedContainer.superEncoder()
var superContainer = superEncoder.container(keyedBy: CodingKeys.self)
try superContainer.encode(one, forKey: .four)
let keyedSuperEncoder = keyedContainer.superEncoder(forKey: .four)
var superKeyedContainer = keyedSuperEncoder.container(keyedBy: CodingKeys.self)
try superKeyedContainer.encode(two, forKey: .five)
var unkeyedContainer = keyedContainer.nestedUnkeyedContainer(forKey: .four)
let unkeyedSuperEncoder = unkeyedContainer.superEncoder()
var keyedUnkeyedSuperContainer = unkeyedSuperEncoder.container(keyedBy: CodingKeys.self)
try keyedUnkeyedSuperContainer.encode(one, forKey: .four)
}
}
private struct ManuallyEncodableStruct: Encodable {
let a = ["string": "string"]
let b = [1, 2, 3]
private enum RootKey: String, CodingKey {
case root
}
private enum TypeKeys: String, CodingKey {
case a, b
}
func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: RootKey.self)
var nestedKeyedContainer = container.nestedContainer(keyedBy: TypeKeys.self, forKey: .root)
try nestedKeyedContainer.encode(a, forKey: .a)
var nestedUnkeyedContainer = container.nestedUnkeyedContainer(forKey: .root)
try nestedUnkeyedContainer.encode(b)
var nestedUnkeyedKeyedContainer = nestedUnkeyedContainer.nestedContainer(keyedBy: TypeKeys.self)
try nestedUnkeyedKeyedContainer.encode(a, forKey: .a)
var nestedUnkeyedUnkeyedContainer = nestedUnkeyedContainer.nestedUnkeyedContainer()
try nestedUnkeyedUnkeyedContainer.encode(b)
}
}
private struct FailingOptionalStruct: Encodable {
enum TestedContainer {
case keyed, unkeyed
}
enum CodingKeys: String, CodingKey { case a }
let testedContainer: TestedContainer
func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch testedContainer {
case .keyed:
var nested = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .a)
try nested.encodeNil(forKey: .a)
case .unkeyed:
var nested = container.nestedUnkeyedContainer(forKey: .a)
try nested.encodeNil()
}
}
}