107 lines
4.1 KiB
Swift
107 lines
4.1 KiB
Swift
|
|
import XCTest
|
||
|
|
@testable import LCEssentials
|
||
|
|
|
||
|
|
final class MultipartFormTests: XCTestCase {
|
||
|
|
|
||
|
|
private func readSerialized(_ form: MultipartForm) throws -> (body: Data, contentType: String, url: URL) {
|
||
|
|
let result = try form.serialize()
|
||
|
|
let data = try Data(contentsOf: result.fileURL)
|
||
|
|
return (data, result.contentType, result.fileURL)
|
||
|
|
}
|
||
|
|
|
||
|
|
private func addCleanup(_ url: URL) {
|
||
|
|
addTeardownBlock { try? FileManager.default.removeItem(at: url) }
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Fields
|
||
|
|
|
||
|
|
func testFieldPartHasNoContentTypeLine() throws {
|
||
|
|
var form = MultipartForm()
|
||
|
|
form.field("caption", "hello world")
|
||
|
|
|
||
|
|
let (body, contentType, url) = try readSerialized(form)
|
||
|
|
addCleanup(url)
|
||
|
|
let text = String(data: body, encoding: .utf8)!
|
||
|
|
|
||
|
|
XCTAssertTrue(contentType.hasPrefix("multipart/form-data; boundary="))
|
||
|
|
let boundary = String(contentType.split(separator: "=", maxSplits: 1)[1])
|
||
|
|
|
||
|
|
XCTAssertEqual(text, [
|
||
|
|
"--\(boundary)\r\n",
|
||
|
|
"Content-Disposition: form-data; name=\"caption\"\r\n",
|
||
|
|
"\r\n",
|
||
|
|
"hello world\r\n",
|
||
|
|
"--\(boundary)--\r\n"
|
||
|
|
].joined())
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - In-memory data file
|
||
|
|
|
||
|
|
func testDataFilePartCarriesFilenameAndMimeAndRawBytes() throws {
|
||
|
|
let png = Data([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0xFF])
|
||
|
|
var form = MultipartForm()
|
||
|
|
form.file("photo", data: png, filename: "p.png")
|
||
|
|
|
||
|
|
let (body, contentType, url) = try readSerialized(form)
|
||
|
|
addCleanup(url)
|
||
|
|
let boundary = String(contentType.split(separator: "=", maxSplits: 1)[1])
|
||
|
|
let text = String(data: body, encoding: .isoLatin1)!
|
||
|
|
|
||
|
|
XCTAssertTrue(text.contains("Content-Disposition: form-data; name=\"photo\"; filename=\"p.png\"\r\n"))
|
||
|
|
XCTAssertTrue(text.contains("Content-Type: image/png\r\n"))
|
||
|
|
XCTAssertTrue(text.hasSuffix("--\(boundary)--\r\n"))
|
||
|
|
|
||
|
|
// raw bytes appear verbatim between the blank line and the trailing CRLF
|
||
|
|
let marker = Data("\r\n\r\n".utf8)
|
||
|
|
let range = body.range(of: marker)!
|
||
|
|
let afterHeader = body[range.upperBound...]
|
||
|
|
XCTAssertTrue(afterHeader.starts(with: png))
|
||
|
|
}
|
||
|
|
|
||
|
|
func testExplicitMimeOverridesGuess() throws {
|
||
|
|
var form = MultipartForm()
|
||
|
|
form.file("f", data: Data("x".utf8), filename: "a.png", mime: "application/octet-stream")
|
||
|
|
let (body, _, url) = try readSerialized(form)
|
||
|
|
addCleanup(url)
|
||
|
|
XCTAssertTrue(String(data: body, encoding: .utf8)!.contains("Content-Type: application/octet-stream\r\n"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Disk file, streamed
|
||
|
|
|
||
|
|
func testDiskFilePartIsStreamedAndContentMatches() throws {
|
||
|
|
let big = Data((0..<(2 * 1024 * 1024)).map { UInt8($0 % 251) })
|
||
|
|
let src = FileManager.default.temporaryDirectory.appendingPathComponent("src-\(UUID().uuidString).bin")
|
||
|
|
try big.write(to: src)
|
||
|
|
addCleanup(src)
|
||
|
|
|
||
|
|
var form = MultipartForm()
|
||
|
|
form.field("kind", "raw")
|
||
|
|
form.file("doc", url: src)
|
||
|
|
|
||
|
|
let (body, contentType, out) = try readSerialized(form)
|
||
|
|
addCleanup(out)
|
||
|
|
let boundary = String(contentType.split(separator: "=", maxSplits: 1)[1])
|
||
|
|
|
||
|
|
// header uses the source filename
|
||
|
|
XCTAssertTrue(String(data: body.prefix(400), encoding: .isoLatin1)!
|
||
|
|
.contains("filename=\"\(src.lastPathComponent)\""))
|
||
|
|
|
||
|
|
// the 2 MB payload is present verbatim
|
||
|
|
let marker = Data("Content-Type: application/octet-stream\r\n\r\n".utf8)
|
||
|
|
let r = body.range(of: marker)!
|
||
|
|
let payload = body[r.upperBound..<(body.index(r.upperBound, offsetBy: big.count))]
|
||
|
|
XCTAssertEqual(Data(payload), big)
|
||
|
|
|
||
|
|
XCTAssertTrue(String(data: body.suffix(boundary.count + 8), encoding: .utf8)!
|
||
|
|
.hasSuffix("--\(boundary)--\r\n"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - MIME lookup
|
||
|
|
|
||
|
|
func testMimeTypeLookup() {
|
||
|
|
XCTAssertEqual(MultipartForm.mimeType(for: "a.jpg"), "image/jpeg")
|
||
|
|
XCTAssertEqual(MultipartForm.mimeType(for: "a.PDF"), "application/pdf")
|
||
|
|
XCTAssertEqual(MultipartForm.mimeType(for: "a.unknownext"), "application/octet-stream")
|
||
|
|
}
|
||
|
|
}
|