// MultipartFormDataTests.swift // // Copyright (c) 2014–2016 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(boundaryType 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: BoundaryType, boundaryKey: String) -> NSData { return BoundaryGenerator.boundary( boundaryType: boundaryType, boundaryKey: boundaryKey ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! } } private func temporaryFileURL() -> NSURL { let tempDirectoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory()) let directoryURL = tempDirectoryURL.URLByAppendingPathComponent("com.alamofire.test/multipart.form.data") let fileManager = NSFileManager.defaultManager() do { try fileManager.createDirectoryAtURL(directoryURL, withIntermediateDirectories: true, attributes: nil) } catch { // No-op - will cause tests to fail, not crash } let fileName = NSUUID().UUIDString let fileURL = directoryURL.URLByAppendingPathComponent(fileName) return fileURL } // 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.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! let data2 = "Vim at integre alterum.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! // When multipartFormData.appendBodyPart(data: data1, name: "data1") multipartFormData.appendBodyPart(data: data2, name: "data2") // Then let expectedContentLength = UInt64(data1.length + data2.length) 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.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! multipartFormData.appendBodyPart(data: data, name: "data") var encodedData: NSData? // 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(boundaryType: .Initial, boundaryKey: boundary) + "Content-Disposition: form-data; name=\"data\"\(CRLF)\(CRLF)" + "Lorem ipsum dolor sit amet." + BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary) ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! XCTAssertEqual(encodedData, expectedData, "encoded data should match expected data") } } func testEncodingMultipleDataBodyParts() { // Given let multipartFormData = MultipartFormData() let french = "français".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! let japanese = "日本語".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! let emoji = "😃👍🏻🍻🎉".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! multipartFormData.appendBodyPart(data: french, name: "french") multipartFormData.appendBodyPart(data: japanese, name: "japanese", mimeType: "text/plain") multipartFormData.appendBodyPart(data: emoji, name: "emoji", mimeType: "text/plain") var encodedData: NSData? // 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(boundaryType: .Initial, boundaryKey: boundary) + "Content-Disposition: form-data; name=\"french\"\(CRLF)\(CRLF)" + "français" + BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) + "Content-Disposition: form-data; name=\"japanese\"\(CRLF)" + "Content-Type: text/plain\(CRLF)\(CRLF)" + "日本語" + BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) + "Content-Disposition: form-data; name=\"emoji\"\(CRLF)" + "Content-Type: text/plain\(CRLF)\(CRLF)" + "😃👍🏻🍻🎉" + BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary) ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! XCTAssertEqual(encodedData, expectedData, "encoded data should match expected data") } } func testEncodingFileBodyPart() { // Given let multipartFormData = MultipartFormData() let unicornImageURL = URLForResource("unicorn", withExtension: "png") multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn") var encodedData: NSData? // 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 = NSMutableData() expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary)) XCTAssertEqual(encodedData, expectedData, "data should match expected data") } } func testEncodingMultipleFileBodyParts() { // Given let multipartFormData = MultipartFormData() let unicornImageURL = URLForResource("unicorn", withExtension: "png") let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg") multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn") multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow") var encodedData: NSData? // 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 = NSMutableData() expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" + "Content-Type: image/jpeg\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!) expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary)) XCTAssertEqual(encodedData, expectedData, "data should match expected data") } } func testEncodingStreamBodyPart() { // Given let multipartFormData = MultipartFormData() let unicornImageURL = URLForResource("unicorn", withExtension: "png") let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length) let unicornStream = NSInputStream(URL: unicornImageURL)! multipartFormData.appendBodyPart( stream: unicornStream, length: unicornDataLength, name: "unicorn", fileName: "unicorn.png", mimeType: "image/png" ) var encodedData: NSData? // 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 = NSMutableData() expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary)) XCTAssertEqual(encodedData, expectedData, "data should match expected data") } } func testEncodingMultipleStreamBodyParts() { // Given let multipartFormData = MultipartFormData() let unicornImageURL = URLForResource("unicorn", withExtension: "png") let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length) let unicornStream = NSInputStream(URL: unicornImageURL)! let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg") let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length) let rainbowStream = NSInputStream(URL: rainbowImageURL)! multipartFormData.appendBodyPart( stream: unicornStream, length: unicornDataLength, name: "unicorn", fileName: "unicorn.png", mimeType: "image/png" ) multipartFormData.appendBodyPart( stream: rainbowStream, length: rainbowDataLength, name: "rainbow", fileName: "rainbow.jpg", mimeType: "image/jpeg" ) var encodedData: NSData? // 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 = NSMutableData() expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" + "Content-Type: image/jpeg\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!) expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary)) XCTAssertEqual(encodedData, expectedData, "data should match expected data") } } func testEncodingMultipleBodyPartsWithVaryingTypes() { // Given let multipartFormData = MultipartFormData() let loremData = "Lorem ipsum.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! let unicornImageURL = URLForResource("unicorn", withExtension: "png") let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg") let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length) let rainbowStream = NSInputStream(URL: rainbowImageURL)! multipartFormData.appendBodyPart(data: loremData, name: "lorem") multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn") multipartFormData.appendBodyPart( stream: rainbowStream, length: rainbowDataLength, name: "rainbow", fileName: "rainbow.jpg", mimeType: "image/jpeg" ) var encodedData: NSData? // 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 = NSMutableData() expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"lorem\"\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(loremData) expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary)) expectedData.appendData(( "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" + "Content-Type: image/jpeg\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!) expectedData.appendData(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.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! multipartFormData.appendBodyPart(data: data, name: "data") var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNil(encodingError, "encoding error should be nil") if let fileData = NSData(contentsOfURL: fileURL) { let boundary = multipartFormData.boundary let expectedFileData = ( BoundaryGenerator.boundary(boundaryType: .Initial, boundaryKey: boundary) + "Content-Disposition: form-data; name=\"data\"\(CRLF)\(CRLF)" + "Lorem ipsum dolor sit amet." + BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary) ).dataUsingEncoding(NSUTF8StringEncoding, 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 french = "français".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! let japanese = "日本語".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! let emoji = "😃👍🏻🍻🎉".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! multipartFormData.appendBodyPart(data: french, name: "french") multipartFormData.appendBodyPart(data: japanese, name: "japanese") multipartFormData.appendBodyPart(data: emoji, name: "emoji") var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNil(encodingError, "encoding error should be nil") if let fileData = NSData(contentsOfURL: fileURL) { let boundary = multipartFormData.boundary let expectedFileData = ( BoundaryGenerator.boundary(boundaryType: .Initial, boundaryKey: boundary) + "Content-Disposition: form-data; name=\"french\"\(CRLF)\(CRLF)" + "français" + BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) + "Content-Disposition: form-data; name=\"japanese\"\(CRLF)\(CRLF)" + "日本語" + BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) + "Content-Disposition: form-data; name=\"emoji\"\(CRLF)\(CRLF)" + "😃👍🏻🍻🎉" + BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary) ).dataUsingEncoding(NSUTF8StringEncoding, 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 = URLForResource("unicorn", withExtension: "png") multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn") var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNil(encodingError, "encoding error should be nil") if let fileData = NSData(contentsOfURL: fileURL) { let boundary = multipartFormData.boundary let expectedFileData = NSMutableData() expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedFileData.appendData(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 = URLForResource("unicorn", withExtension: "png") let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg") multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn") multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow") var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNil(encodingError, "encoding error should be nil") if let fileData = NSData(contentsOfURL: fileURL) { let boundary = multipartFormData.boundary let expectedFileData = NSMutableData() expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" + "Content-Type: image/jpeg\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!) expectedFileData.appendData(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 = URLForResource("unicorn", withExtension: "png") let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length) let unicornStream = NSInputStream(URL: unicornImageURL)! multipartFormData.appendBodyPart( stream: unicornStream, length: unicornDataLength, name: "unicorn", fileName: "unicorn.png", mimeType: "image/png" ) var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNil(encodingError, "encoding error should be nil") if let fileData = NSData(contentsOfURL: fileURL) { let boundary = multipartFormData.boundary let expectedFileData = NSMutableData() expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedFileData.appendData(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 = URLForResource("unicorn", withExtension: "png") let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length) let unicornStream = NSInputStream(URL: unicornImageURL)! let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg") let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length) let rainbowStream = NSInputStream(URL: rainbowImageURL)! multipartFormData.appendBodyPart( stream: unicornStream, length: unicornDataLength, name: "unicorn", fileName: "unicorn.png", mimeType: "image/png" ) multipartFormData.appendBodyPart( stream: rainbowStream, length: rainbowDataLength, name: "rainbow", fileName: "rainbow.jpg", mimeType: "image/jpeg" ) var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNil(encodingError, "encoding error should be nil") if let fileData = NSData(contentsOfURL: fileURL) { let boundary = multipartFormData.boundary let expectedFileData = NSMutableData() expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" + "Content-Type: image/jpeg\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!) expectedFileData.appendData(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.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! let unicornImageURL = URLForResource("unicorn", withExtension: "png") let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg") let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length) let rainbowStream = NSInputStream(URL: rainbowImageURL)! multipartFormData.appendBodyPart(data: loremData, name: "lorem") multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn") multipartFormData.appendBodyPart( stream: rainbowStream, length: rainbowDataLength, name: "rainbow", fileName: "rainbow.jpg", mimeType: "image/jpeg" ) var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNil(encodingError, "encoding error should be nil") if let fileData = NSData(contentsOfURL: fileURL) { let boundary = multipartFormData.boundary let expectedFileData = NSMutableData() expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"lorem\"\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(loremData) expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" + "Content-Type: image/png\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!) expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary)) expectedFileData.appendData(( "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" + "Content-Type: image/jpeg\(CRLF)\(CRLF)" ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! ) expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!) expectedFileData.appendData(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: "")! let multipartFormData = MultipartFormData() multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data") var encodingError: NSError? // When do { try multipartFormData.encode() } catch { encodingError = error as NSError } // Then XCTAssertNotNil(encodingError, "encoding error should not be nil") if let error = encodingError { XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value") XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value") if let failureReason = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String { let expectedFailureReason = "Failed to extract the fileName of the provided URL: \(fileURL)" XCTAssertEqual(failureReason, expectedFailureReason, "failure reason does not match expected value") } else { XCTFail("failure reason should not be nil") } } } func testThatAppendingFileBodyPartThatIsNotFileURLReturnsError() { // Given let fileURL = NSURL(string: "https://example.com/image.jpg")! let multipartFormData = MultipartFormData() multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data") var encodingError: NSError? // When do { try multipartFormData.encode() } catch { encodingError = error as NSError } // Then XCTAssertNotNil(encodingError, "encoding error should not be nil") if let error = encodingError { XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value") XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value") if let failureReason = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String { let expectedFailureReason = "The file URL does not point to a file URL: \(fileURL)" XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value") } else { XCTFail("failure reason should not be nil") } } } func testThatAppendingFileBodyPartThatIsNotReachableReturnsError() { // Given let filePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("does_not_exist.jpg") let fileURL = NSURL(fileURLWithPath: filePath) let multipartFormData = MultipartFormData() multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data") var encodingError: NSError? // When do { try multipartFormData.encode() } catch { encodingError = error as NSError } // Then XCTAssertNotNil(encodingError, "encoding error should not be nil") if let error = encodingError { XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value") XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value") if let failureReason = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String { let expectedFailureReason = "The file URL is not reachable: \(fileURL)" XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value") } else { XCTFail("failure reason should not be nil") } } } func testThatAppendingFileBodyPartThatIsDirectoryReturnsError() { // Given let directoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) let multipartFormData = MultipartFormData() multipartFormData.appendBodyPart(fileURL: directoryURL, name: "empty_data") var encodingError: NSError? // When do { try multipartFormData.encode() } catch { encodingError = error as NSError } // Then XCTAssertNotNil(encodingError, "encoding error should not be nil") if let error = encodingError { XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value") XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value") if let failureReason = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String { let expectedFailureReason = "The file URL is a directory, not a file: \(directoryURL)" XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value") } else { XCTFail("failure reason should not be nil") } } } func testThatWritingEncodedDataToExistingFileURLFails() { // Given let fileURL = temporaryFileURL() var writerError: NSError? do { try "dummy data".writeToURL(fileURL, atomically: true, encoding: NSUTF8StringEncoding) } catch { writerError = error as NSError } let multipartFormData = MultipartFormData() let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! multipartFormData.appendBodyPart(data: data, name: "data") var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNil(writerError, "writer error should be nil") XCTAssertNotNil(encodingError, "encoding error should not be nil") if let encodingError = encodingError { XCTAssertEqual(encodingError.domain, "com.alamofire.error", "encoding error domain does not match expected value") XCTAssertEqual(encodingError.code, NSURLErrorBadURL, "encoding error code does not match expected value") } } func testThatWritingEncodedDataToBadURLFails() { // Given let fileURL = NSURL(string: "/this/is/not/a/valid/url")! let multipartFormData = MultipartFormData() let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! multipartFormData.appendBodyPart(data: data, name: "data") var encodingError: NSError? // When do { try multipartFormData.writeEncodedDataToDisk(fileURL) } catch { encodingError = error as NSError } // Then XCTAssertNotNil(encodingError, "encoding error should not be nil") if let encodingError = encodingError { XCTAssertEqual(encodingError.domain, "com.alamofire.error", "encoding error domain does not match expected value") XCTAssertEqual(encodingError.code, NSURLErrorBadURL, "encoding error code does not match expected value") } } }