964 lines
36 KiB
Swift
964 lines
36 KiB
Swift
//
|
|
// MultipartFormDataTests.swift
|
|
//
|
|
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
import Alamofire
|
|
import Foundation
|
|
import XCTest
|
|
|
|
struct EncodingCharacters {
|
|
static let crlf = "\r\n"
|
|
}
|
|
|
|
struct BoundaryGenerator {
|
|
enum BoundaryType {
|
|
case initial, encapsulated, final
|
|
}
|
|
|
|
static func boundary(forBoundaryType boundaryType: BoundaryType, boundaryKey: String) -> String {
|
|
let boundary: String
|
|
|
|
switch boundaryType {
|
|
case .initial:
|
|
boundary = "--\(boundaryKey)\(EncodingCharacters.crlf)"
|
|
case .encapsulated:
|
|
boundary = "\(EncodingCharacters.crlf)--\(boundaryKey)\(EncodingCharacters.crlf)"
|
|
case .final:
|
|
boundary = "\(EncodingCharacters.crlf)--\(boundaryKey)--\(EncodingCharacters.crlf)"
|
|
}
|
|
|
|
return boundary
|
|
}
|
|
|
|
static func boundaryData(boundaryType: BoundaryType, boundaryKey: String) -> Data {
|
|
return BoundaryGenerator.boundary(
|
|
forBoundaryType: boundaryType,
|
|
boundaryKey: boundaryKey
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
}
|
|
}
|
|
|
|
private func temporaryFileURL() -> URL { return BaseTestCase.testDirectoryURL.appendingPathComponent(UUID().uuidString) }
|
|
|
|
// MARK: -
|
|
|
|
class MultipartFormDataPropertiesTestCase: BaseTestCase {
|
|
func testThatContentTypeContainsBoundary() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
// When
|
|
let boundary = multipartFormData.boundary
|
|
|
|
// Then
|
|
let expectedContentType = "multipart/form-data; boundary=\(boundary)"
|
|
XCTAssertEqual(multipartFormData.contentType, expectedContentType, "contentType should match expected value")
|
|
}
|
|
|
|
func testThatContentLengthMatchesTotalBodyPartSize() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
let data1 = "Lorem ipsum dolor sit amet.".data(using: .utf8, allowLossyConversion: false)!
|
|
let data2 = "Vim at integre alterum.".data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
// When
|
|
multipartFormData.append(data1, withName: "data1")
|
|
multipartFormData.append(data2, withName: "data2")
|
|
|
|
// Then
|
|
let expectedContentLength = UInt64(data1.count + data2.count)
|
|
XCTAssertEqual(multipartFormData.contentLength, expectedContentLength, "content length should match expected value")
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
class MultipartFormDataEncodingTestCase: BaseTestCase {
|
|
let crlf = EncodingCharacters.crlf
|
|
|
|
func testEncodingDataBodyPart() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let data = "Lorem ipsum dolor sit amet.".data(using: .utf8, allowLossyConversion: false)!
|
|
multipartFormData.append(data, withName: "data")
|
|
|
|
var encodedData: Data?
|
|
|
|
// When
|
|
do {
|
|
encodedData = try multipartFormData.encode()
|
|
} catch {
|
|
// No-op
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodedData, "encoded data should not be nil")
|
|
|
|
if let encodedData = encodedData {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
let expectedData = (
|
|
BoundaryGenerator.boundary(forBoundaryType: .initial, boundaryKey: boundary) +
|
|
"Content-Disposition: form-data; name=\"data\"\(crlf)\(crlf)" +
|
|
"Lorem ipsum dolor sit amet." +
|
|
BoundaryGenerator.boundary(forBoundaryType: .final, boundaryKey: boundary)
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
XCTAssertEqual(encodedData, expectedData, "encoded data should match expected data")
|
|
}
|
|
}
|
|
|
|
func testEncodingMultipleDataBodyParts() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let frenchData = "français".data(using: .utf8, allowLossyConversion: false)!
|
|
let japaneseData = "日本語".data(using: .utf8, allowLossyConversion: false)!
|
|
let emojiData = "😃👍🏻🍻🎉".data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
multipartFormData.append(frenchData, withName: "french")
|
|
multipartFormData.append(japaneseData, withName: "japanese", mimeType: "text/plain")
|
|
multipartFormData.append(emojiData, withName: "emoji", mimeType: "text/plain")
|
|
|
|
var encodedData: Data?
|
|
|
|
// When
|
|
do {
|
|
encodedData = try multipartFormData.encode()
|
|
} catch {
|
|
// No-op
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodedData, "encoded data should not be nil")
|
|
|
|
if let encodedData = encodedData {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
let expectedData = (
|
|
BoundaryGenerator.boundary(forBoundaryType: .initial, boundaryKey: boundary) +
|
|
"Content-Disposition: form-data; name=\"french\"\(crlf)\(crlf)" +
|
|
"français" +
|
|
BoundaryGenerator.boundary(forBoundaryType: .encapsulated, boundaryKey: boundary) +
|
|
"Content-Disposition: form-data; name=\"japanese\"\(crlf)" +
|
|
"Content-Type: text/plain\(crlf)\(crlf)" +
|
|
"日本語" +
|
|
BoundaryGenerator.boundary(forBoundaryType: .encapsulated, boundaryKey: boundary) +
|
|
"Content-Disposition: form-data; name=\"emoji\"\(crlf)" +
|
|
"Content-Type: text/plain\(crlf)\(crlf)" +
|
|
"😃👍🏻🍻🎉" +
|
|
BoundaryGenerator.boundary(forBoundaryType: .final, boundaryKey: boundary)
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
XCTAssertEqual(encodedData, expectedData, "encoded data should match expected data")
|
|
}
|
|
}
|
|
|
|
func testEncodingFileBodyPart() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
multipartFormData.append(unicornImageURL, withName: "unicorn")
|
|
|
|
var encodedData: Data?
|
|
|
|
// When
|
|
do {
|
|
encodedData = try multipartFormData.encode()
|
|
} catch {
|
|
// No-op
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodedData, "encoded data should not be nil")
|
|
|
|
if let encodedData = encodedData {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedData = Data()
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(encodedData, expectedData, "data should match expected data")
|
|
}
|
|
}
|
|
|
|
func testEncodingMultipleFileBodyParts() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
let rainbowImageURL = url(forResource: "rainbow", withExtension: "jpg")
|
|
|
|
multipartFormData.append(unicornImageURL, withName: "unicorn")
|
|
multipartFormData.append(rainbowImageURL, withName: "rainbow")
|
|
|
|
var encodedData: Data?
|
|
|
|
// When
|
|
do {
|
|
encodedData = try multipartFormData.encode()
|
|
} catch {
|
|
// No-op
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodedData, "encoded data should not be nil")
|
|
|
|
if let encodedData = encodedData {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedData = Data()
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(crlf)" +
|
|
"Content-Type: image/jpeg\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(try! Data(contentsOf: rainbowImageURL))
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(encodedData, expectedData, "data should match expected data")
|
|
}
|
|
}
|
|
|
|
func testEncodingStreamBodyPart() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
let unicornDataLength = UInt64((try! Data(contentsOf: unicornImageURL)).count)
|
|
let unicornStream = InputStream(url: unicornImageURL)!
|
|
|
|
multipartFormData.append(
|
|
unicornStream,
|
|
withLength: unicornDataLength,
|
|
name: "unicorn",
|
|
fileName: "unicorn.png",
|
|
mimeType: "image/png"
|
|
)
|
|
|
|
var encodedData: Data?
|
|
|
|
// When
|
|
do {
|
|
encodedData = try multipartFormData.encode()
|
|
} catch {
|
|
// No-op
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodedData, "encoded data should not be nil")
|
|
|
|
if let encodedData = encodedData {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedData = Data()
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(encodedData, expectedData, "data should match expected data")
|
|
}
|
|
}
|
|
|
|
func testEncodingMultipleStreamBodyParts() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
let unicornDataLength = UInt64((try! Data(contentsOf: unicornImageURL)).count)
|
|
let unicornStream = InputStream(url: unicornImageURL)!
|
|
|
|
let rainbowImageURL = url(forResource: "rainbow", withExtension: "jpg")
|
|
let rainbowDataLength = UInt64((try! Data(contentsOf: rainbowImageURL)).count)
|
|
let rainbowStream = InputStream(url: rainbowImageURL)!
|
|
|
|
multipartFormData.append(
|
|
unicornStream,
|
|
withLength: unicornDataLength,
|
|
name: "unicorn",
|
|
fileName: "unicorn.png",
|
|
mimeType: "image/png"
|
|
)
|
|
multipartFormData.append(
|
|
rainbowStream,
|
|
withLength: rainbowDataLength,
|
|
name: "rainbow",
|
|
fileName: "rainbow.jpg",
|
|
mimeType: "image/jpeg"
|
|
)
|
|
|
|
var encodedData: Data?
|
|
|
|
// When
|
|
do {
|
|
encodedData = try multipartFormData.encode()
|
|
} catch {
|
|
// No-op
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodedData, "encoded data should not be nil")
|
|
|
|
if let encodedData = encodedData {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedData = Data()
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(crlf)" +
|
|
"Content-Type: image/jpeg\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(try! Data(contentsOf: rainbowImageURL))
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(encodedData, expectedData, "data should match expected data")
|
|
}
|
|
}
|
|
|
|
func testEncodingMultipleBodyPartsWithVaryingTypes() {
|
|
// Given
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let loremData = "Lorem ipsum.".data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
|
|
let rainbowImageURL = url(forResource: "rainbow", withExtension: "jpg")
|
|
let rainbowDataLength = UInt64((try! Data(contentsOf: rainbowImageURL)).count)
|
|
let rainbowStream = InputStream(url: rainbowImageURL)!
|
|
|
|
multipartFormData.append(loremData, withName: "lorem")
|
|
multipartFormData.append(unicornImageURL, withName: "unicorn")
|
|
multipartFormData.append(
|
|
rainbowStream,
|
|
withLength: rainbowDataLength,
|
|
name: "rainbow",
|
|
fileName: "rainbow.jpg",
|
|
mimeType: "image/jpeg"
|
|
)
|
|
|
|
var encodedData: Data?
|
|
|
|
// When
|
|
do {
|
|
encodedData = try multipartFormData.encode()
|
|
} catch {
|
|
// No-op
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodedData, "encoded data should not be nil")
|
|
|
|
if let encodedData = encodedData {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedData = Data()
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"lorem\"\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(loremData)
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundaryKey: boundary))
|
|
expectedData.append((
|
|
"Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(crlf)" +
|
|
"Content-Type: image/jpeg\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedData.append(try! Data(contentsOf: rainbowImageURL))
|
|
expectedData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(encodedData, expectedData, "data should match expected data")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
class MultipartFormDataWriteEncodedDataToDiskTestCase: BaseTestCase {
|
|
let crlf = EncodingCharacters.crlf
|
|
|
|
func testWritingEncodedDataBodyPartToDisk() {
|
|
// Given
|
|
let fileURL = temporaryFileURL()
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let data = "Lorem ipsum dolor sit amet.".data(using: .utf8, allowLossyConversion: false)!
|
|
multipartFormData.append(data, withName: "data")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNil(encodingError, "encoding error should be nil")
|
|
|
|
if let fileData = try? Data(contentsOf: fileURL) {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
let expectedFileData = (
|
|
BoundaryGenerator.boundary(forBoundaryType: .initial, boundaryKey: boundary) +
|
|
"Content-Disposition: form-data; name=\"data\"\(crlf)\(crlf)" +
|
|
"Lorem ipsum dolor sit amet." +
|
|
BoundaryGenerator.boundary(forBoundaryType: .final, boundaryKey: boundary)
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
|
|
} else {
|
|
XCTFail("file data should not be nil")
|
|
}
|
|
}
|
|
|
|
func testWritingMultipleEncodedDataBodyPartsToDisk() {
|
|
// Given
|
|
let fileURL = temporaryFileURL()
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let frenchData = "français".data(using: .utf8, allowLossyConversion: false)!
|
|
let japaneseData = "日本語".data(using: .utf8, allowLossyConversion: false)!
|
|
let emojiData = "😃👍🏻🍻🎉".data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
multipartFormData.append(frenchData, withName: "french")
|
|
multipartFormData.append(japaneseData, withName: "japanese")
|
|
multipartFormData.append(emojiData, withName: "emoji")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNil(encodingError, "encoding error should be nil")
|
|
|
|
if let fileData = try? Data(contentsOf: fileURL) {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
let expectedFileData = (
|
|
BoundaryGenerator.boundary(forBoundaryType: .initial, boundaryKey: boundary) +
|
|
"Content-Disposition: form-data; name=\"french\"\(crlf)\(crlf)" +
|
|
"français" +
|
|
BoundaryGenerator.boundary(forBoundaryType: .encapsulated, boundaryKey: boundary) +
|
|
"Content-Disposition: form-data; name=\"japanese\"\(crlf)\(crlf)" +
|
|
"日本語" +
|
|
BoundaryGenerator.boundary(forBoundaryType: .encapsulated, boundaryKey: boundary) +
|
|
"Content-Disposition: form-data; name=\"emoji\"\(crlf)\(crlf)" +
|
|
"😃👍🏻🍻🎉" +
|
|
BoundaryGenerator.boundary(forBoundaryType: .final, boundaryKey: boundary)
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
|
|
} else {
|
|
XCTFail("file data should not be nil")
|
|
}
|
|
}
|
|
|
|
func testWritingEncodedFileBodyPartToDisk() {
|
|
// Given
|
|
let fileURL = temporaryFileURL()
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
multipartFormData.append(unicornImageURL, withName: "unicorn")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNil(encodingError, "encoding error should be nil")
|
|
|
|
if let fileData = try? Data(contentsOf: fileURL) {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedFileData = Data()
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
|
|
} else {
|
|
XCTFail("file data should not be nil")
|
|
}
|
|
}
|
|
|
|
func testWritingMultipleEncodedFileBodyPartsToDisk() {
|
|
// Given
|
|
let fileURL = temporaryFileURL()
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
let rainbowImageURL = url(forResource: "rainbow", withExtension: "jpg")
|
|
|
|
multipartFormData.append(unicornImageURL, withName: "unicorn")
|
|
multipartFormData.append(rainbowImageURL, withName: "rainbow")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNil(encodingError, "encoding error should be nil")
|
|
|
|
if let fileData = try? Data(contentsOf: fileURL) {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedFileData = Data()
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(crlf)" +
|
|
"Content-Type: image/jpeg\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(try! Data(contentsOf: rainbowImageURL))
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
|
|
} else {
|
|
XCTFail("file data should not be nil")
|
|
}
|
|
}
|
|
|
|
func testWritingEncodedStreamBodyPartToDisk() {
|
|
// Given
|
|
let fileURL = temporaryFileURL()
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
let unicornDataLength = UInt64((try! Data(contentsOf: unicornImageURL)).count)
|
|
let unicornStream = InputStream(url: unicornImageURL)!
|
|
|
|
multipartFormData.append(
|
|
unicornStream,
|
|
withLength: unicornDataLength,
|
|
name: "unicorn",
|
|
fileName: "unicorn.png",
|
|
mimeType: "image/png"
|
|
)
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNil(encodingError, "encoding error should be nil")
|
|
|
|
if let fileData = try? Data(contentsOf: fileURL) {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedFileData = Data()
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
|
|
} else {
|
|
XCTFail("file data should not be nil")
|
|
}
|
|
}
|
|
|
|
func testWritingMultipleEncodedStreamBodyPartsToDisk() {
|
|
// Given
|
|
let fileURL = temporaryFileURL()
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
let unicornDataLength = UInt64((try! Data(contentsOf: unicornImageURL)).count)
|
|
let unicornStream = InputStream(url: unicornImageURL)!
|
|
|
|
let rainbowImageURL = url(forResource: "rainbow", withExtension: "jpg")
|
|
let rainbowDataLength = UInt64((try! Data(contentsOf: rainbowImageURL)).count)
|
|
let rainbowStream = InputStream(url: rainbowImageURL)!
|
|
|
|
multipartFormData.append(
|
|
unicornStream,
|
|
withLength: unicornDataLength,
|
|
name: "unicorn",
|
|
fileName: "unicorn.png",
|
|
mimeType: "image/png"
|
|
)
|
|
multipartFormData.append(
|
|
rainbowStream,
|
|
withLength: rainbowDataLength,
|
|
name: "rainbow",
|
|
fileName: "rainbow.jpg",
|
|
mimeType: "image/jpeg"
|
|
)
|
|
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNil(encodingError, "encoding error should be nil")
|
|
|
|
if let fileData = try? Data(contentsOf: fileURL) {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedFileData = Data()
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(crlf)" +
|
|
"Content-Type: image/jpeg\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(try! Data(contentsOf: rainbowImageURL))
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
|
|
} else {
|
|
XCTFail("file data should not be nil")
|
|
}
|
|
}
|
|
|
|
func testWritingMultipleEncodedBodyPartsWithVaryingTypesToDisk() {
|
|
// Given
|
|
let fileURL = temporaryFileURL()
|
|
let multipartFormData = MultipartFormData()
|
|
|
|
let loremData = "Lorem ipsum.".data(using: .utf8, allowLossyConversion: false)!
|
|
|
|
let unicornImageURL = url(forResource: "unicorn", withExtension: "png")
|
|
|
|
let rainbowImageURL = url(forResource: "rainbow", withExtension: "jpg")
|
|
let rainbowDataLength = UInt64((try! Data(contentsOf: rainbowImageURL)).count)
|
|
let rainbowStream = InputStream(url: rainbowImageURL)!
|
|
|
|
multipartFormData.append(loremData, withName: "lorem")
|
|
multipartFormData.append(unicornImageURL, withName: "unicorn")
|
|
multipartFormData.append(
|
|
rainbowStream,
|
|
withLength: rainbowDataLength,
|
|
name: "rainbow",
|
|
fileName: "rainbow.jpg",
|
|
mimeType: "image/jpeg"
|
|
)
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNil(encodingError, "encoding error should be nil")
|
|
|
|
if let fileData = try? Data(contentsOf: fileURL) {
|
|
let boundary = multipartFormData.boundary
|
|
|
|
var expectedFileData = Data()
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .initial, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"lorem\"\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(loremData)
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(crlf)" +
|
|
"Content-Type: image/png\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(try! Data(contentsOf: unicornImageURL))
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundaryKey: boundary))
|
|
expectedFileData.append((
|
|
"Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(crlf)" +
|
|
"Content-Type: image/jpeg\(crlf)\(crlf)"
|
|
).data(using: .utf8, allowLossyConversion: false)!
|
|
)
|
|
expectedFileData.append(try! Data(contentsOf: rainbowImageURL))
|
|
expectedFileData.append(BoundaryGenerator.boundaryData(boundaryType: .final, boundaryKey: boundary))
|
|
|
|
XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
|
|
} else {
|
|
XCTFail("file data should not be nil")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
class MultipartFormDataFailureTestCase: BaseTestCase {
|
|
func testThatAppendingFileBodyPartWithInvalidLastPathComponentReturnsError() {
|
|
// Given
|
|
let fileURL = NSURL(string: "")! as URL
|
|
let multipartFormData = MultipartFormData()
|
|
multipartFormData.append(fileURL, withName: "empty_data")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
_ = try multipartFormData.encode()
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodingError, "encoding error should not be nil")
|
|
|
|
if let error = encodingError as? AFError {
|
|
XCTAssertTrue(error.isBodyPartFilenameInvalid)
|
|
|
|
let expectedFailureReason = "The URL provided does not have a valid filename: \(fileURL)"
|
|
XCTAssertEqual(error.localizedDescription, expectedFailureReason, "failure reason does not match expected value")
|
|
} else {
|
|
XCTFail("Error should be AFError.")
|
|
}
|
|
}
|
|
|
|
func testThatAppendingFileBodyPartThatIsNotFileURLReturnsError() {
|
|
// Given
|
|
let fileURL = URL(string: "https://example.com/image.jpg")!
|
|
let multipartFormData = MultipartFormData()
|
|
multipartFormData.append(fileURL, withName: "empty_data")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
_ = try multipartFormData.encode()
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodingError, "encoding error should not be nil")
|
|
|
|
if let error = encodingError as? AFError {
|
|
XCTAssertTrue(error.isBodyPartURLInvalid)
|
|
|
|
let expectedFailureReason = "The URL provided is not a file URL: \(fileURL)"
|
|
XCTAssertEqual(error.localizedDescription, expectedFailureReason, "error failure reason does not match expected value")
|
|
} else {
|
|
XCTFail("Error should be AFError.")
|
|
}
|
|
}
|
|
|
|
func testThatAppendingFileBodyPartThatIsNotReachableReturnsError() {
|
|
// Given
|
|
let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent("does_not_exist.jpg")
|
|
let fileURL = URL(fileURLWithPath: filePath)
|
|
let multipartFormData = MultipartFormData()
|
|
multipartFormData.append(fileURL, withName: "empty_data")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
_ = try multipartFormData.encode()
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodingError, "encoding error should not be nil")
|
|
|
|
if let error = encodingError as? AFError {
|
|
XCTAssertTrue(error.isBodyPartFileNotReachableWithError)
|
|
} else {
|
|
XCTFail("Error should be AFError.")
|
|
}
|
|
}
|
|
|
|
func testThatAppendingFileBodyPartThatIsDirectoryReturnsError() {
|
|
// Given
|
|
let directoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
|
let multipartFormData = MultipartFormData()
|
|
multipartFormData.append(directoryURL, withName: "empty_data", fileName: "empty", mimeType: "application/octet")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
_ = try multipartFormData.encode()
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodingError, "encoding error should not be nil")
|
|
|
|
if let error = encodingError as? AFError {
|
|
XCTAssertTrue(error.isBodyPartFileIsDirectory)
|
|
|
|
let expectedFailureReason = "The URL provided is a directory: \(directoryURL)"
|
|
XCTAssertEqual(error.localizedDescription, expectedFailureReason, "error failure reason does not match expected value")
|
|
} else {
|
|
XCTFail("Error should be AFError.")
|
|
}
|
|
}
|
|
|
|
func testThatWritingEncodedDataToExistingFileURLFails() {
|
|
// Given
|
|
let fileURL = temporaryFileURL()
|
|
|
|
var writerError: Error?
|
|
|
|
do {
|
|
try "dummy data".write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
|
|
} catch {
|
|
writerError = error
|
|
}
|
|
|
|
let multipartFormData = MultipartFormData()
|
|
let data = "Lorem ipsum dolor sit amet.".data(using: .utf8, allowLossyConversion: false)!
|
|
multipartFormData.append(data, withName: "data")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNil(writerError, "writer error should be nil")
|
|
XCTAssertNotNil(encodingError, "encoding error should not be nil")
|
|
|
|
if let encodingError = encodingError as? AFError {
|
|
XCTAssertTrue(encodingError.isOutputStreamFileAlreadyExists)
|
|
}
|
|
}
|
|
|
|
func testThatWritingEncodedDataToBadURLFails() {
|
|
// Given
|
|
let fileURL = URL(string: "/this/is/not/a/valid/url")!
|
|
|
|
let multipartFormData = MultipartFormData()
|
|
let data = "Lorem ipsum dolor sit amet.".data(using: .utf8, allowLossyConversion: false)!
|
|
multipartFormData.append(data, withName: "data")
|
|
|
|
var encodingError: Error?
|
|
|
|
// When
|
|
do {
|
|
try multipartFormData.writeEncodedData(to: fileURL)
|
|
} catch {
|
|
encodingError = error
|
|
}
|
|
|
|
// Then
|
|
XCTAssertNotNil(encodingError, "encoding error should not be nil")
|
|
|
|
if let encodingError = encodingError as? AFError {
|
|
XCTAssertTrue(encodingError.isOutputStreamURLInvalid)
|
|
}
|
|
}
|
|
}
|