Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.x.x Rename HummingbirdXCT to HummingbirdTesting #392

Merged
merged 3 commits into from Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Benchmarks/Benchmarks/Router/RouterBenchmarks.swift
Expand Up @@ -16,7 +16,7 @@ import Benchmark
import HTTPTypes
import Hummingbird
import NIOHTTPTypes
@_spi(HBXCT) import HummingbirdCore
@_spi(HBInternal) import HummingbirdCore
import Logging
import NIOCore
import NIOPosix
Expand Down
12 changes: 6 additions & 6 deletions Package.swift
Expand Up @@ -15,7 +15,7 @@ let package = Package(
.library(name: "HummingbirdTLS", targets: ["HummingbirdTLS"]),
.library(name: "HummingbirdJobs", targets: ["HummingbirdJobs"]),
.library(name: "HummingbirdRouter", targets: ["HummingbirdRouter"]),
.library(name: "HummingbirdXCT", targets: ["HummingbirdXCT"]),
.library(name: "HummingbirdTesting", targets: ["HummingbirdTesting"]),
.executable(name: "PerformanceTest", targets: ["PerformanceTest"]),
],
dependencies: [
Expand Down Expand Up @@ -89,7 +89,7 @@ let package = Package(
swiftSettings: swiftSettings
),
.target(
name: "HummingbirdXCT",
name: "HummingbirdTesting",
dependencies: [
.byName(name: "Hummingbird"),
.product(name: "AsyncHTTPClient", package: "async-http-client"),
Expand Down Expand Up @@ -138,16 +138,16 @@ let package = Package(
.byName(name: "Hummingbird"),
.byName(name: "HummingbirdTLS"),
.byName(name: "HummingbirdHTTP2"),
.byName(name: "HummingbirdXCT"),
.byName(name: "HummingbirdTesting"),
]),
.testTarget(name: "HummingbirdJobsTests", dependencies: [
.byName(name: "HummingbirdJobs"),
.byName(name: "HummingbirdXCT"),
.byName(name: "HummingbirdTesting"),
.product(name: "Atomics", package: "swift-atomics"),
]),
.testTarget(name: "HummingbirdRouterTests", dependencies: [
.byName(name: "HummingbirdRouter"),
.byName(name: "HummingbirdXCT"),
.byName(name: "HummingbirdTesting"),
]),
.testTarget(
name: "HummingbirdCoreTests",
Expand All @@ -156,7 +156,7 @@ let package = Package(
.byName(name: "HummingbirdCore"),
.byName(name: "HummingbirdHTTP2"),
.byName(name: "HummingbirdTLS"),
.byName(name: "HummingbirdXCT"),
.byName(name: "HummingbirdTesting"),
.product(name: "AsyncHTTPClient", package: "async-http-client"),
],
resources: [.process("Certificates")]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -49,7 +49,7 @@ Hummingbird is designed to require the least number of dependencies possible, bu
- `HummingbirdRouter`: an alternative router that uses a resultbuilder.
- `HummingbirdTLS`: TLS support.
- `HummingbirdHTTP2`: Support for HTTP2 upgrades.
- `HummingbirdXCT`: helper functions to aid testing Hummingbird projects.
- `HummingbirdTesting`: helper functions to aid testing Hummingbird projects.

And also the following are available in other repositories in this organisation

Expand Down
Expand Up @@ -17,27 +17,27 @@ import HummingbirdCore
import NIOCore

/// HTTP Scheme to use with AsyncHTTPClient test framework
public enum XCTScheme: String {
public enum HBTestHTTPScheme: String {
case http
case https
}

/// Type of test framework
public struct XCTTestingSetup {
public struct HBTestingSetup {
enum Internal {
case router
case live
case ahc(XCTScheme)
case ahc(HBTestHTTPScheme)
}

let value: Internal

/// Test writing requests directly to router.
public static var router: XCTTestingSetup { .init(value: .router) }
public static var router: HBTestingSetup { .init(value: .router) }
/// Sets up a live server and execute tests using a HTTP client. Only supports HTTP1
public static var live: XCTTestingSetup { .init(value: .live) }
public static var live: HBTestingSetup { .init(value: .live) }
/// Sets up a live server and execute tests using a HTTP client. Does not support trailer headers
public static func ahc(_ scheme: XCTScheme) -> XCTTestingSetup { .init(value: .ahc(scheme)) }
public static func ahc(_ scheme: HBTestHTTPScheme) -> HBTestingSetup { .init(value: .ahc(scheme)) }
}

/// Extends `HBApplicationProtocol` to support testing of applications
Expand All @@ -46,7 +46,7 @@ extension HBApplicationProtocol where Responder.Context: HBRequestContext {

/// Test `HBApplication`
///
/// You use `test` and `XCTExecute` to test applications. You can either test using
/// You use `test` and `execute` to test applications. You can either test using
/// the `.router` test framework which sends requests directly to the router for testing your code or
/// the `.live` or `.ahc` frameworks which both run live servers to pass requests to, but provide
/// a single connection HTTP client or AsyncHTTPClient as a client respectively . The `.router` test
Expand All @@ -62,7 +62,7 @@ extension HBApplicationProtocol where Responder.Context: HBRequestContext {
/// let app = HBApplication(router: router)
/// app.test(.router) { client in
/// // does my app return "hello" in the body for this route
/// client.XCTExecute(uri: "/hello", method: .GET) { response in
/// client.execute(uri: "/hello", method: .GET) { response in
/// XCTAssertEqual(String(buffer: response.body), "hello")
/// }
/// }
Expand All @@ -72,13 +72,13 @@ extension HBApplicationProtocol where Responder.Context: HBRequestContext {
/// - testing: indicates which type of testing framework we want
/// - configuration: configuration of application
public func test<Value>(
_ testingSetup: XCTTestingSetup,
_ test: @escaping @Sendable (any HBXCTClientProtocol) async throws -> Value
_ testingSetup: HBTestingSetup,
_ test: @escaping @Sendable (any HBTestClientProtocol) async throws -> Value
) async throws -> Value {
let app: any HBXCTApplication = switch testingSetup.value {
case .router: try await HBXCTRouter(app: self)
case .live: HBXCTLive(app: self)
case .ahc(let scheme): HBXCTAsyncHTTPClient(app: self, scheme: scheme)
let app: any HBApplicationTestFramework = switch testingSetup.value {
case .router: try await HBRouterTestFramework(app: self)
case .live: HBLiveTestFramework(app: self)
case .ahc(let scheme): HBAsyncHTTPClientTestFramework(app: self, scheme: scheme)
}
return try await app.run(test)
}
Expand Down
Expand Up @@ -17,8 +17,8 @@
import NIOCore
import ServiceLifecycle

/// Response structure returned by XCT testing framework
public struct HBXCTResponse: Sendable {
/// Response structure returned by testing framework
public struct HBTestResponse: Sendable {
public let head: HTTPResponse
/// response status
public var status: HTTPResponse.Status { self.head.status }
Expand All @@ -30,8 +30,8 @@
public let trailerHeaders: HTTPFields?
}

/// Errors thrown by XCT framework.
struct HBXCTError: Error, Equatable {
/// Errors thrown by testing framework.
struct HBTestError: Error, Equatable {
private enum _Internal {
case notStarted
case noHead
Expand All @@ -52,18 +52,18 @@
static var timeout: Self { .init(.timeout) }
}

/// Protocol for client used by HummingbirdXCT
public protocol HBXCTClientProtocol: Sendable {
/// Protocol for client used by HummingbirdTesting
public protocol HBTestClientProtocol: Sendable {
/// Execute URL request and provide response
func execute(
func executeRequest(
uri: String,
method: HTTPRequest.Method,
headers: HTTPFields,
body: ByteBuffer?
) async throws -> HBXCTResponse
) async throws -> HBTestResponse
}

extension HBXCTClientProtocol {
extension HBTestClientProtocol {
/// Send request to associated test framework and call test callback on the response returned
///
/// - Parameters:
Expand All @@ -73,23 +73,23 @@
/// - body: Request body
/// - testCallback: closure to call on response returned by test framework
/// - Returns: Return value of test closure
@discardableResult public func XCTExecute<Return>(
@discardableResult public func execute<Return>(
uri: String,
method: HTTPRequest.Method,
headers: HTTPFields = [:],
body: ByteBuffer? = nil,
testCallback: @escaping (HBXCTResponse) async throws -> Return = { $0 }
testCallback: @escaping (HBTestResponse) async throws -> Return = { $0 }

Check warning on line 81 in Sources/HummingbirdTesting/ApplicationTester.swift

View check run for this annotation

Codecov / codecov/patch

Sources/HummingbirdTesting/ApplicationTester.swift#L81

Added line #L81 was not covered by tests
) async throws -> Return {
let response = try await execute(uri: uri, method: method, headers: headers, body: body)
let response = try await executeRequest(uri: uri, method: method, headers: headers, body: body)
return try await testCallback(response)
}
}

/// Protocol for Test application.
protocol HBXCTApplication {
/// Associated client with XCT server type
associatedtype Client: HBXCTClientProtocol
/// Protocol for application test framework
protocol HBApplicationTestFramework {
/// Associated client for application test
associatedtype Client: HBTestClientProtocol

/// Run XCT server
func run<Value>(_ test: @escaping @Sendable (any HBXCTClientProtocol) async throws -> Value) async throws -> Value
/// Run test server
func run<Value>(_ test: @escaping @Sendable (any HBTestClientProtocol) async throws -> Value) async throws -> Value
}
Expand Up @@ -25,19 +25,19 @@ import ServiceLifecycle
import XCTest

/// Test using a live server and AsyncHTTPClient as a client
final class HBXCTAsyncHTTPClient<App: HBApplicationProtocol>: HBXCTApplication {
struct Client: HBXCTClientProtocol {
final class HBAsyncHTTPClientTestFramework<App: HBApplicationProtocol>: HBApplicationTestFramework {
struct Client: HBTestClientProtocol {
let client: HTTPClient
let urlPrefix: String
let timeout: TimeAmount

/// Send request and call test callback on the response returned
func execute(
func executeRequest(
uri: String,
method: HTTPRequest.Method,
headers: HTTPFields = [:],
body: ByteBuffer? = nil
) async throws -> HBXCTResponse {
) async throws -> HBTestResponse {
let url = "\(self.urlPrefix)\(uri.first == "/" ? "" : "/")\(uri)"
var request = HTTPClientRequest(url: url)
request.method = .init(method)
Expand All @@ -49,14 +49,14 @@ final class HBXCTAsyncHTTPClient<App: HBApplicationProtocol>: HBXCTApplication {
}
}

init(app: App, scheme: XCTScheme) {
init(app: App, scheme: HBTestHTTPScheme) {
self.timeout = .seconds(15)
self.application = TestApplication(base: app)
self.scheme = scheme
}

/// Start tests
func run<Value>(_ test: @escaping @Sendable (HBXCTClientProtocol) async throws -> Value) async throws -> Value {
func run<Value>(_ test: @escaping @Sendable (HBTestClientProtocol) async throws -> Value) async throws -> Value {
try await withThrowingTaskGroup(of: Void.self) { group in
let serviceGroup = ServiceGroup(
configuration: .init(
Expand Down Expand Up @@ -90,7 +90,7 @@ final class HBXCTAsyncHTTPClient<App: HBApplicationProtocol>: HBXCTApplication {
}

let application: TestApplication<App>
let scheme: XCTScheme
let scheme: HBTestHTTPScheme
let timeout: TimeAmount
}

Expand Down
Expand Up @@ -23,20 +23,20 @@ import ServiceLifecycle
import XCTest

/// Test using a live server
final class HBXCTLive<App: HBApplicationProtocol>: HBXCTApplication {
struct Client: HBXCTClientProtocol {
let client: HBXCTClient
final class HBLiveTestFramework<App: HBApplicationProtocol>: HBApplicationTestFramework {
struct Client: HBTestClientProtocol {
let client: HBTestClient

/// Send request and call test callback on the response returned
func execute(
func executeRequest(
uri: String,
method: HTTPRequest.Method,
headers: HTTPFields = [:],
body: ByteBuffer? = nil
) async throws -> HBXCTResponse {
) async throws -> HBTestResponse {
var headers = headers
headers[.connection] = "keep-alive"
let request = HBXCTClient.Request(uri, method: method, authority: "localhost", headers: headers, body: body)
let request = HBTestClient.Request(uri, method: method, authority: "localhost", headers: headers, body: body)
let response = try await client.execute(request)
return .init(head: response.head, body: response.body ?? ByteBuffer(), trailerHeaders: response.trailerHeaders)
}
Expand All @@ -48,7 +48,7 @@ final class HBXCTLive<App: HBApplicationProtocol>: HBXCTApplication {
}

/// Start tests
func run<Value>(_ test: @escaping @Sendable (HBXCTClientProtocol) async throws -> Value) async throws -> Value {
func run<Value>(_ test: @escaping @Sendable (HBTestClientProtocol) async throws -> Value) async throws -> Value {
try await withThrowingTaskGroup(of: Void.self) { group in
let serviceGroup = ServiceGroup(
configuration: .init(
Expand All @@ -61,7 +61,7 @@ final class HBXCTLive<App: HBApplicationProtocol>: HBXCTApplication {
try await serviceGroup.run()
}
let port = await self.application.portPromise.wait()
let client = HBXCTClient(
let client = HBTestClient(
host: "localhost",
port: port,
configuration: .init(timeout: self.timeout),
Expand Down
Expand Up @@ -15,8 +15,8 @@
import Atomics
import HTTPTypes
import NIOEmbedded
@_spi(HBXCT) import Hummingbird
@_spi(HBXCT) import HummingbirdCore
@_spi(HBInternal) import Hummingbird
@_spi(HBInternal) import HummingbirdCore
import Logging
import NIOConcurrencyHelpers
import NIOCore
Expand All @@ -25,7 +25,7 @@ import NIOPosix
import ServiceLifecycle

/// Test sending requests directly to router. This does not setup a live server
struct HBXCTRouter<Responder: HBResponder>: HBXCTApplication where Responder.Context: HBBaseRequestContext {
struct HBRouterTestFramework<Responder: HBResponder>: HBApplicationTestFramework where Responder.Context: HBBaseRequestContext {
let responder: Responder
let makeContext: @Sendable (Logger) -> Responder.Context
let services: [any Service]
Expand All @@ -46,7 +46,7 @@ struct HBXCTRouter<Responder: HBResponder>: HBXCTApplication where Responder.Con
}

/// Run test
func run<Value>(_ test: @escaping @Sendable (HBXCTClientProtocol) async throws -> Value) async throws -> Value {
func run<Value>(_ test: @escaping @Sendable (HBTestClientProtocol) async throws -> Value) async throws -> Value {
let client = Client(
responder: self.responder,
logger: self.logger,
Expand Down Expand Up @@ -88,15 +88,15 @@ struct HBXCTRouter<Responder: HBResponder>: HBXCTApplication where Responder.Con
}
}

/// HBXCTRouter client. Constructs an `HBRequest` sends it to the router and then converts
/// resulting response back to XCT response type
struct Client: HBXCTClientProtocol {
/// HBRouterTestFramework client. Constructs an `HBRequest` sends it to the router and then converts
/// resulting response back to test response type
struct Client: HBTestClientProtocol {
let responder: Responder
let logger: Logger
let makeContext: @Sendable (Logger) -> Responder.Context

func execute(uri: String, method: HTTPRequest.Method, headers: HTTPFields, body: ByteBuffer?) async throws -> HBXCTResponse {
return try await withThrowingTaskGroup(of: HBXCTResponse.self) { group in
func executeRequest(uri: String, method: HTTPRequest.Method, headers: HTTPFields, body: ByteBuffer?) async throws -> HBTestResponse {
return try await withThrowingTaskGroup(of: HBTestResponse.self) { group in
let (stream, source) = HBRequestBody.makeStream()
let request = HBRequest(
head: .init(method: method, scheme: "http", authority: "localhost", path: uri, headerFields: headers),
Expand All @@ -118,7 +118,7 @@ struct HBXCTRouter<Responder: HBResponder>: HBXCTApplication where Responder.Con
let responseWriter = RouterResponseWriter()
let trailerHeaders = try await response.body.write(responseWriter)
return responseWriter.collated.withLockedValue { collated in
HBXCTResponse(head: response.head, body: collated, trailerHeaders: trailerHeaders)
HBTestResponse(head: response.head, body: collated, trailerHeaders: trailerHeaders)
}
}

Expand Down
Expand Up @@ -21,7 +21,7 @@ import ServiceLifecycle
/// TestApplication used to wrap HBApplication being tested.
///
/// This is needed to override the `onServerRunning` function
struct TestApplication<BaseApp: HBApplicationProtocol>: HBApplicationProtocol, Service {
internal struct TestApplication<BaseApp: HBApplicationProtocol>: HBApplicationProtocol, Service {
typealias Responder = BaseApp.Responder
typealias ChildChannel = BaseApp.ChildChannel

Expand Down
Expand Up @@ -17,7 +17,7 @@ import HTTPTypes
import NIOCore

/// HTTP client types
extension HBXCTClient {
extension HBTestClient {
public enum Error: Swift.Error {
case invalidURL
case malformedResponse
Expand Down