diff --git a/Package.swift b/Package.swift index b52ea72..9a61899 100644 --- a/Package.swift +++ b/Package.swift @@ -1,15 +1,17 @@ -// swift-tools-version: 5.7.1 +// swift-tools-version: 5.10 import PackageDescription let package = Package( name: "swift-http-types", + platforms: [.macOS(.v10_15)], products: [ .library(name: "HTTPTypes", targets: ["HTTPTypes"]), .library(name: "HTTPTypesFoundation", targets: ["HTTPTypesFoundation"]), ], dependencies: [ .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"), + .package(url: "https://github.com/apple/swift-testing.git", from: "0.11.0"), ], targets: [ .target(name: "HTTPTypes"), @@ -23,12 +25,14 @@ let package = Package( name: "HTTPTypesTests", dependencies: [ "HTTPTypes", + .product(name: "Testing", package: "swift-testing"), ] ), .testTarget( name: "HTTPTypesFoundationTests", dependencies: [ "HTTPTypesFoundation", + .product(name: "Testing", package: "swift-testing"), ] ), ] diff --git a/Tests/HTTPTypesFoundationTests/HTTPTypesFoundationTests.swift b/Tests/HTTPTypesFoundationTests/HTTPTypesFoundationTests.swift index 42f4a24..a7bd04c 100644 --- a/Tests/HTTPTypesFoundationTests/HTTPTypesFoundationTests.swift +++ b/Tests/HTTPTypesFoundationTests/HTTPTypesFoundationTests.swift @@ -12,47 +12,51 @@ // //===----------------------------------------------------------------------===// +import Foundation import HTTPTypes import HTTPTypesFoundation -import XCTest +import Testing #if canImport(FoundationNetworking) import FoundationNetworking #endif -final class HTTPTypesFoundationTests: XCTestCase { - func testRequestURLParsing() { +@Suite("HTTPTypesFoundationTests") +struct HTTPTypesFoundationTests { + @Test("Request URL parses correctly") + func requestURLParsing() { let request1 = HTTPRequest(url: URL(string: "h://a")!) - XCTAssertEqual(request1.scheme, "h") - XCTAssertEqual(request1.authority, "a") - XCTAssertEqual(request1.path, "/") - XCTAssertEqual(request1.url?.absoluteString, "h://a/") + #expect(request1.scheme == "h") + #expect(request1.authority == "a") + #expect(request1.path == "/") + #expect(request1.url?.absoluteString == "h://a/") let request2 = HTTPRequest(url: URL(string: "h://a:4?")!) - XCTAssertEqual(request2.scheme, "h") - XCTAssertEqual(request2.authority, "a:4") - XCTAssertEqual(request2.path, "/?") - XCTAssertEqual(request2.url?.absoluteString, "h://a:4/?") + #expect(request2.scheme == "h") + #expect(request2.authority == "a:4") + #expect(request2.path == "/?") + #expect(request2.url?.absoluteString == "h://a:4/?") let request3 = HTTPRequest(url: URL(string: "h://a/")!) - XCTAssertEqual(request3.scheme, "h") - XCTAssertEqual(request3.authority, "a") - XCTAssertEqual(request3.path, "/") - XCTAssertEqual(request3.url?.absoluteString, "h://a/") + #expect(request3.scheme == "h") + #expect(request3.authority == "a") + #expect(request3.path == "/") + #expect(request3.url?.absoluteString == "h://a/") let request4 = HTTPRequest(url: URL(string: "h://a/p?q#1")!) - XCTAssertEqual(request4.scheme, "h") - XCTAssertEqual(request4.authority, "a") - XCTAssertEqual(request4.path, "/p?q") - XCTAssertEqual(request4.url?.absoluteString, "h://a/p?q") + #expect(request4.scheme == "h") + #expect(request4.authority == "a") + #expect(request4.path == "/p?q") + #expect(request4.url?.absoluteString == "h://a/p?q") let request5 = HTTPRequest(url: URL(string: "data:,Hello%2C%20World%21")!) - XCTAssertEqual(request5.scheme, "data") - XCTAssertNil(request5.authority) - XCTAssertEqual(request5.path, "/") - XCTAssertNil(request5.url) + #expect(request5.scheme == "data") + #expect(request5.authority == nil) + #expect(request5.path == "/") + #expect(request5.url == nil) } - func testRequestToFoundation() throws { + @Test("Request converts to Foundation") + func requestToFoundation() throws { let request = HTTPRequest( method: .get, scheme: "https", authority: "www.example.com", path: "/", headerFields: [ @@ -64,27 +68,29 @@ final class HTTPTypesFoundationTests: XCTestCase { ] ) - let urlRequest = try XCTUnwrap(URLRequest(httpRequest: request)) - XCTAssertEqual(urlRequest.url, URL(string: "https://www.example.com/")!) - XCTAssertEqual(urlRequest.value(forHTTPHeaderField: "aCcEpT"), "*/*") - XCTAssertEqual(urlRequest.value(forHTTPHeaderField: "Accept-Encoding"), "gzip, br") - XCTAssertEqual(urlRequest.value(forHTTPHeaderField: "cookie"), "a=b; c=d") + let urlRequest = try #require(URLRequest(httpRequest: request)) + #expect(urlRequest.url == URL(string: "https://www.example.com/")!) + #expect(urlRequest.value(forHTTPHeaderField: "aCcEpT") == "*/*") + #expect(urlRequest.value(forHTTPHeaderField: "Accept-Encoding") == "gzip, br") + #expect(urlRequest.value(forHTTPHeaderField: "cookie") == "a=b; c=d") } - func testRequestFromFoundation() throws { + @Test("Request creates from Foundation") + func requestFromFoundation() throws { var urlRequest = URLRequest(url: URL(string: "https://www.example.com/")!) urlRequest.httpMethod = "POST" urlRequest.setValue("Bar", forHTTPHeaderField: "X-Foo") - let request = try XCTUnwrap(urlRequest.httpRequest) - XCTAssertEqual(request.method, .post) - XCTAssertEqual(request.scheme, "https") - XCTAssertEqual(request.authority, "www.example.com") - XCTAssertEqual(request.path, "/") - XCTAssertEqual(request.headerFields[.init("x-foo")!], "Bar") + let request = try #require(urlRequest.httpRequest) + #expect(request.method == .post) + #expect(request.scheme == "https") + #expect(request.authority == "www.example.com") + #expect(request.path == "/") + #expect(request.headerFields[.init("x-foo")!] == "Bar") } - func testResponseToFoundation() throws { + @Test("Response converts to Foundation") + func responseToFoundation() throws { let response = HTTPResponse( status: .ok, headerFields: [ @@ -92,12 +98,13 @@ final class HTTPTypesFoundationTests: XCTestCase { ] ) - let urlResponse = try XCTUnwrap(HTTPURLResponse(httpResponse: response, url: URL(string: "https://www.example.com/")!)) - XCTAssertEqual(urlResponse.statusCode, 200) - XCTAssertEqual(urlResponse.value(forHTTPHeaderField: "Server"), "HTTPServer/1.0") + let urlResponse = try #require(HTTPURLResponse(httpResponse: response, url: URL(string: "https://www.example.com/")!)) + #expect(urlResponse.statusCode == 200) + #expect(urlResponse.value(forHTTPHeaderField: "Server") == "HTTPServer/1.0") } - func testResponseFromFoundation() throws { + @Test("Response creates from Foundation") + func responseFromFoundation() throws { let urlResponse = HTTPURLResponse( url: URL(string: "https://www.example.com/")!, statusCode: 204, httpVersion: nil, headerFields: [ @@ -105,8 +112,8 @@ final class HTTPTypesFoundationTests: XCTestCase { ] )! - let response = try XCTUnwrap(urlResponse.httpResponse) - XCTAssertEqual(response.status, .noContent) - XCTAssertEqual(response.headerFields[.init("X-EMOJI")!], "😀") + let response = try #require(urlResponse.httpResponse) + #expect(response.status == .noContent) + #expect(response.headerFields[.init("X-EMOJI")!] == "😀") } } diff --git a/Tests/HTTPTypesTests/HTTPTypesTests.swift b/Tests/HTTPTypesTests/HTTPTypesTests.swift index cb2852d..39c3afb 100644 --- a/Tests/HTTPTypesTests/HTTPTypesTests.swift +++ b/Tests/HTTPTypesTests/HTTPTypesTests.swift @@ -12,8 +12,9 @@ // //===----------------------------------------------------------------------===// +import Foundation import HTTPTypes -import XCTest +import Testing extension HTTPField.Name { static let acceptEncodingLower = HTTPField.Name("accept-encoding")! @@ -22,26 +23,30 @@ extension HTTPField.Name { static let acceptLanguageUpper = HTTPField.Name("ACCEPT-LANGUAGE")! } -final class HTTPTypesTests: XCTestCase { - func testFields() { +@Suite("HTTPTypesTests") +struct HTTPTypesTests { + @Test("Fields behave correctly") + func fields() { var fields = HTTPFields() fields[.acceptEncoding] = "gzip" fields.append(HTTPField(name: .acceptEncodingLower, value: "br")) fields.insert(HTTPField(name: .acceptEncodingMixed, value: "deflate"), at: 1) - XCTAssertEqual(fields[.acceptEncoding], "gzip, deflate, br") - XCTAssertEqual(fields[values: .acceptEncodingUpper].count, 3) + #expect(fields[.acceptEncoding] == "gzip, deflate, br") + #expect(fields[values: .acceptEncodingUpper].count == 3) } - func testFieldValue() { - XCTAssertEqual(HTTPField(name: .accept, value: " \n 😀 \t ").value, "😀") - XCTAssertEqual(HTTPField(name: .accept, value: " a 😀 \t\n b \t \r ").value, "a 😀 \t b") - XCTAssertEqual(HTTPField(name: .accept, value: "").value, "") - XCTAssertFalse(HTTPField.isValidValue(" ")) - XCTAssertEqual(HTTPField(name: .accept, lenientValue: " \r\n\0\t ".utf8).value, " \t ") + @Test("Field values are processed correctly") + func fieldValue() { + #expect(HTTPField(name: .accept, value: " \n 😀 \t ").value == "😀") + #expect(HTTPField(name: .accept, value: " a 😀 \t\n b \t \r ").value == "a 😀 \t b") + #expect(HTTPField(name: .accept, value: "").value == "") + #expect(!HTTPField.isValidValue(" ")) + #expect(HTTPField(name: .accept, lenientValue: " \r\n\0\t ".utf8).value == " \t ") } - func testRequest() { + @Test("Requests are created and compared correctly") + func request() { var request1 = HTTPRequest(method: .get, scheme: "https", authority: "www.example.com", path: "/") request1.headerFields = [ .acceptLanguage: "en", @@ -49,11 +54,12 @@ final class HTTPTypesTests: XCTestCase { var request2 = HTTPRequest(method: HTTPRequest.Method("GET")!, scheme: "https", authority: "www.example.com", path: "/") request2.headerFields.append(HTTPField(name: .acceptLanguageUpper, value: "en")) - XCTAssertEqual(request2.method, .get) - XCTAssertEqual(request1, request2) + #expect(request2.method == .get) + #expect(request1 == request2) } - func testResponse() { + @Test("Responses are created and modified correctly") + func response() { var response1 = HTTPResponse(status: 200) response1.headerFields = [ .server: "HTTPServer/1.0", @@ -64,60 +70,62 @@ final class HTTPTypesTests: XCTestCase { response2.status = .movedPermanently response2.headerFields.append(HTTPField(name: .location, value: "https://www.example.com/new")) - XCTAssertEqual(response1.status, .ok) - XCTAssertEqual(response1.status.kind, .successful) - XCTAssertEqual(response1.headerFields.count, 2) + #expect(response1.status == .ok) + #expect(response1.status.kind == .successful) + #expect(response1.headerFields.count == 2) - XCTAssertEqual(response2.status, 301) - XCTAssertEqual(response2.status.kind, .redirection) - XCTAssertEqual(response2.headerFields.count, 3) - XCTAssertEqual(response2.headerFields[.server], "HTTPServer/1.0") + #expect(response2.status == 301) + #expect(response2.status.kind == .redirection) + #expect(response2.headerFields.count == 3) + #expect(response2.headerFields[.server] == "HTTPServer/1.0") } - func testComparison() { + @Test("HTTP fields are compared correctly") + func comparison() { let fields1: HTTPFields = [ .acceptEncoding: "br", .acceptEncoding: "gzip", .accept: "*/*", ] - XCTAssertNotEqual(fields1, [:]) + #expect(fields1 != [:]) let fields2: HTTPFields = [ .acceptEncoding: "br", .acceptEncoding: "gzip", .accept: "*/*", ] - XCTAssertEqual(fields1, fields2) + #expect(fields1 == fields2) let fields3: HTTPFields = [ .acceptEncoding: "br", .accept: "*/*", .acceptEncoding: "gzip", ] - XCTAssertEqual(fields1, fields3) + #expect(fields1 == fields3) let fields4: HTTPFields = [ .acceptEncoding: "br", .accept: "*/*", ] - XCTAssertNotEqual(fields1, fields4) + #expect(fields1 != fields4) let fields5: HTTPFields = [ .acceptEncoding: "gzip", .acceptEncoding: "br", .accept: "*/*", ] - XCTAssertNotEqual(fields1, fields5) + #expect(fields1 != fields5) let fields6: HTTPFields = [ .acceptEncoding: "gzip", .acceptEncoding: "br", .acceptLanguage: "en", ] - XCTAssertNotEqual(fields1, fields6) + #expect(fields1 != fields6) } - func testSendable() { + @Test("Types conform to Sendable protocol") + func sendable() { func isSendable(_ value: some Sendable) -> Bool { true } func isSendable(_ value: Any) -> Bool { false } @@ -132,19 +140,20 @@ final class HTTPTypesTests: XCTestCase { let status: HTTPResponse.Status = response.status let responsePseudoHeaderFields: HTTPResponse.PseudoHeaderFields = response.pseudoHeaderFields - XCTAssertTrue(isSendable(field)) - XCTAssertTrue(isSendable(indexingStrategy)) - XCTAssertTrue(isSendable(name)) - XCTAssertTrue(isSendable(fields)) - XCTAssertTrue(isSendable(request)) - XCTAssertTrue(isSendable(method)) - XCTAssertTrue(isSendable(requestPseudoHeaderFields)) - XCTAssertTrue(isSendable(response)) - XCTAssertTrue(isSendable(status)) - XCTAssertTrue(isSendable(responsePseudoHeaderFields)) + #expect(isSendable(field)) + #expect(isSendable(indexingStrategy)) + #expect(isSendable(name)) + #expect(isSendable(fields)) + #expect(isSendable(request)) + #expect(isSendable(method)) + #expect(isSendable(requestPseudoHeaderFields)) + #expect(isSendable(response)) + #expect(isSendable(status)) + #expect(isSendable(responsePseudoHeaderFields)) } - func testRequestCoding() throws { + @Test("Requests are encoded and decoded correctly") + func requestCoding() throws { let request = HTTPRequest(method: .put, scheme: "https", authority: "www.example.com", path: "/upload", headerFields: [ .acceptEncoding: "br", .acceptEncoding: "gzip", @@ -153,7 +162,7 @@ final class HTTPTypesTests: XCTestCase { let encoded = try JSONEncoder().encode(request) let json = try JSONSerialization.jsonObject(with: encoded) - XCTAssertEqual(json as? NSDictionary, [ + #expect(json as? NSDictionary == [ "pseudoHeaderFields": [ ["name": ":method", "value": "PUT"], ["name": ":scheme", "value": "https"], @@ -168,10 +177,11 @@ final class HTTPTypesTests: XCTestCase { ]) let decoded = try JSONDecoder().decode(HTTPRequest.self, from: encoded) - XCTAssertEqual(request, decoded) + #expect(request == decoded) } - func testResponseCoding() throws { + @Test("Responses are encoded and decoded correctly") + func responseCoding() throws { var response = HTTPResponse(status: .noContent, headerFields: [ .server: "HTTPServer/1.0", ]) @@ -179,7 +189,7 @@ final class HTTPTypesTests: XCTestCase { let encoded = try JSONEncoder().encode(response) let json = try JSONSerialization.jsonObject(with: encoded) - XCTAssertEqual(json as? NSDictionary, [ + #expect(json as? NSDictionary == [ "pseudoHeaderFields": [ ["name": ":status", "value": "204"], ], @@ -190,10 +200,11 @@ final class HTTPTypesTests: XCTestCase { ]) let decoded = try JSONDecoder().decode(HTTPResponse.self, from: encoded) - XCTAssertEqual(response, decoded) + #expect(response == decoded) } - func testRequestParsing() throws { + @Test("Requests are parsed correctly") + func requestParsing() throws { let fields = [ HTTPField(name: HTTPField.Name(parsed: ":method")!, lenientValue: "PUT".utf8), HTTPField(name: HTTPField.Name(parsed: ":scheme")!, lenientValue: "https".utf8), @@ -202,30 +213,32 @@ final class HTTPTypesTests: XCTestCase { HTTPField(name: HTTPField.Name(parsed: "content-length")!, lenientValue: "1024".utf8), ] let request = try HTTPRequest(parsed: fields) - XCTAssertEqual(request.method, .put) - XCTAssertEqual(request.scheme, "https") - XCTAssertEqual(request.authority, "www.example.com") - XCTAssertEqual(request.path, "/upload") - XCTAssertEqual(request.headerFields[.contentLength], "1024") + #expect(request.method == .put) + #expect(request.scheme == "https") + #expect(request.authority == "www.example.com") + #expect(request.path == "/upload") + #expect(request.headerFields[.contentLength] == "1024") } - func testResponseParsing() throws { + @Test("Responses are parsed correctly") + func responseParsing() throws { let fields = [ HTTPField(name: HTTPField.Name(parsed: ":status")!, lenientValue: "204".utf8), HTTPField(name: HTTPField.Name(parsed: "server")!, lenientValue: "HTTPServer/1.0".utf8), ] let response = try HTTPResponse(parsed: fields) - XCTAssertEqual(response.status, .noContent) - XCTAssertEqual(response.headerFields[.server], "HTTPServer/1.0") + #expect(response.status == .noContent) + #expect(response.headerFields[.server] == "HTTPServer/1.0") } - func testTrailerFieldsParsing() throws { + @Test("Trailer fields are parsed correctly") + func trailerFieldsParsing() throws { let fields = [ HTTPField(name: HTTPField.Name(parsed: "trailer1")!, lenientValue: "value1".utf8), HTTPField(name: HTTPField.Name(parsed: "trailer2")!, lenientValue: "value2".utf8), ] let trailerFields = try HTTPFields(parsedTrailerFields: fields) - XCTAssertEqual(trailerFields[HTTPField.Name("trailer1")!], "value1") - XCTAssertEqual(trailerFields[HTTPField.Name("trailer2")!], "value2") + #expect(trailerFields[HTTPField.Name("trailer1")!] == "value1") + #expect(trailerFields[HTTPField.Name("trailer2")!] == "value2") } }