Add assertion helpers for NIOHTTP1TestServer (#1760)
Motivation: Verifying the contents of inbound request parts for `NIOHTTP1TestServer` can be quite tedious and whenever I use `NIOHTTP1TestServer` in different projects I find myself writing similar extensions to help verify the inbound request parts are as expected. Modifications: - Add verification methods to `NIOHTTP1TestServer` which checks for the expected part and optionally verifies it further in a callback Result: It's easier to test with NIOHTTP1TestServer
This commit is contained in:
parent
bd41bd5cf5
commit
d22d89804c
|
@ -135,21 +135,22 @@ private final class AggregateBodyHandler: ChannelInboundHandler {
|
|||
/// body: requestBody))
|
||||
///
|
||||
/// // Assert the server received the expected request.
|
||||
/// // Use custom methods if you only want some specific assertions on part
|
||||
/// // of the request.
|
||||
/// XCTAssertNoThrow(XCTAssertEqual(.head(.init(version: .http1_1,
|
||||
/// method: .GET,
|
||||
/// uri: "/some-route",
|
||||
/// headers: .init([
|
||||
/// ("Content-Type", "text/plain; charset=utf-8"),
|
||||
/// ("Content-Length", "4")]))),
|
||||
/// try testServer.readInbound()))
|
||||
/// XCTAssertNoThrow(try testServer.receiveHeadAndVerify { head in
|
||||
/// XCTAssertEqual(head, .init(version: .http1_1,
|
||||
/// method: .GET,
|
||||
/// uri: "/some-route",
|
||||
/// headers: .init([
|
||||
/// ("Content-Type", "text/plain; charset=utf-8"),
|
||||
/// ("Content-Length", "4")])))
|
||||
/// })
|
||||
/// var requestBuffer = allocator.buffer(capacity: 128)
|
||||
/// requestBuffer.writeString(requestBody)
|
||||
/// XCTAssertNoThrow(XCTAssertEqual(.body(requestBuffer),
|
||||
/// try testServer.readInbound()))
|
||||
/// XCTAssertNoThrow(XCTAssertEqual(.end(nil),
|
||||
/// try testServer.readInbound()))
|
||||
/// XCTAssertNoThrow(try testServer.receiveBodyAndVerify { body in
|
||||
/// XCTAssertEqual(body, requestBuffer)
|
||||
/// })
|
||||
/// XCTAssertNoThrow(try testServer.receiveEndAndVerify { trailers in
|
||||
/// XCTAssertNil(trailers)
|
||||
/// })
|
||||
///
|
||||
/// // Make the server send a response to the client.
|
||||
/// let responseBody = "pong"
|
||||
|
@ -314,3 +315,105 @@ extension NIOHTTP1TestServer {
|
|||
self.inboundBuffer.append(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
extension NIOHTTP1TestServer {
|
||||
/// Waits for a message part to be received and checks that it was a `.head` before returning
|
||||
/// the `HTTPRequestHead` it contained.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - deadline: The deadline by which a part must have been received.
|
||||
/// - Throws: If the part was not a `.head` or nothing was read before the deadline.
|
||||
/// - Returns: The `HTTPRequestHead` from the `.head`.
|
||||
public func receiveHead(deadline: NIODeadline = .now() + .seconds(10)) throws -> HTTPRequestHead {
|
||||
let part = try self.readInbound(deadline: deadline)
|
||||
switch part {
|
||||
case .head(let head):
|
||||
return head
|
||||
default:
|
||||
throw NIOHTTP1TestServerError(reason: "Expected .head but got '\(part)'")
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits for a message part to be received and checks that it was a `.head` before passing
|
||||
/// it to the `verify` block.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - deadline: The deadline by which a part must have been received.
|
||||
/// - verify: A closure which can be used to verify the contents of the `HTTPRequestHead`.
|
||||
/// - Throws: If the part was not a `.head` or nothing was read before the deadline.
|
||||
public func receiveHeadAndVerify(deadline: NIODeadline = .now() + .seconds(10),
|
||||
_ verify: (HTTPRequestHead) throws -> () = { _ in }) throws {
|
||||
try verify(self.receiveHead(deadline: deadline))
|
||||
}
|
||||
|
||||
/// Waits for a message part to be received and checks that it was a `.body` before returning
|
||||
/// the `ByteBuffer` it contained.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - deadline: The deadline by which a part must have been received.
|
||||
/// - Throws: If the part was not a `.body` or nothing was read before the deadline.
|
||||
/// - Returns: The `ByteBuffer` from the `.body`.
|
||||
public func receiveBody(deadline: NIODeadline = .now() + .seconds(10)) throws -> ByteBuffer {
|
||||
let part = try self.readInbound(deadline: deadline)
|
||||
switch part {
|
||||
case .body(let buffer):
|
||||
return buffer
|
||||
default:
|
||||
throw NIOHTTP1TestServerError(reason: "Expected .body but got '\(part)'")
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits for a message part to be received and checks that it was a `.body` before passing
|
||||
/// it to the `verify` block.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - deadline: The deadline by which a part must have been received.
|
||||
/// - verify: A closure which can be used to verify the contents of the `ByteBuffer`.
|
||||
/// - Throws: If the part was not a `.body` or nothing was read before the deadline.
|
||||
public func receiveBodyAndVerify(deadline: NIODeadline = .now() + .seconds(10),
|
||||
_ verify: (ByteBuffer) throws -> () = { _ in }) throws {
|
||||
try verify(self.receiveBody(deadline: deadline))
|
||||
}
|
||||
|
||||
|
||||
/// Waits for a message part to be received and checks that it was a `.end` before returning
|
||||
/// the `HTTPHeaders?` it contained.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - deadline: The deadline by which a part must have been received.
|
||||
/// - Throws: If the part was not a `.end` or nothing was read before the deadline.
|
||||
/// - Returns: The `HTTPHeaders?` from the `.end`.
|
||||
public func receiveEnd(deadline: NIODeadline = .now() + .seconds(10)) throws -> HTTPHeaders? {
|
||||
let part = try self.readInbound(deadline: deadline)
|
||||
switch part {
|
||||
case .end(let trailers):
|
||||
return trailers
|
||||
default:
|
||||
throw NIOHTTP1TestServerError(reason: "Expected .end but got '\(part)'")
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits for a message part to be received and checks that it was a `.end` before passing
|
||||
/// it to the `verify` block.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - deadline: The deadline by which a part must have been received.
|
||||
/// - verify: A closure which can be used to verify the contents of the `HTTPHeaders?`.
|
||||
/// - Throws: If the part was not a `.end` or nothing was read before the deadline.
|
||||
public func receiveEndAndVerify(deadline: NIODeadline = .now() + .seconds(10),
|
||||
_ verify: (HTTPHeaders?) throws -> () = { _ in }) throws {
|
||||
try verify(self.receiveEnd())
|
||||
}
|
||||
}
|
||||
|
||||
public struct NIOHTTP1TestServerError: Error, Hashable, CustomStringConvertible {
|
||||
public var reason: String
|
||||
|
||||
public init(reason: String) {
|
||||
self.reason = reason
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return self.reason
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ extension NIOHTTP1TestServerTest {
|
|||
("testConcurrentRequests", testConcurrentRequests),
|
||||
("testTestWebServerCanBeReleased", testTestWebServerCanBeReleased),
|
||||
("testStopClosesAcceptedChannel", testStopClosesAcceptedChannel),
|
||||
("testReceiveAndVerify", testReceiveAndVerify),
|
||||
("testReceive", testReceive),
|
||||
("testReceiveAndVerifyWrongPart", testReceiveAndVerifyWrongPart),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,81 @@ class NIOHTTP1TestServerTest: XCTestCase {
|
|||
XCTAssertNotNil(channel)
|
||||
XCTAssertNoThrow(try channel.closeFuture.wait())
|
||||
}
|
||||
|
||||
func testReceiveAndVerify() {
|
||||
let testServer = NIOHTTP1TestServer(group: self.group)
|
||||
|
||||
let responsePromise = self.group.next().makePromise(of: String.self)
|
||||
var channel: Channel!
|
||||
XCTAssertNoThrow(channel = try self.connect(serverPort: testServer.serverPort,
|
||||
responsePromise: responsePromise).wait())
|
||||
self.sendRequest(channel: channel, uri: "/uri", message: "hello")
|
||||
|
||||
XCTAssertNoThrow(try testServer.receiveHeadAndVerify { head in
|
||||
XCTAssertEqual(head.uri, "/uri")
|
||||
})
|
||||
|
||||
XCTAssertNoThrow(try testServer.receiveBodyAndVerify { buffer in
|
||||
XCTAssertEqual(buffer, ByteBuffer(string: "hello"))
|
||||
})
|
||||
|
||||
XCTAssertNoThrow(try testServer.receiveEndAndVerify { trailers in
|
||||
XCTAssertNil(trailers)
|
||||
})
|
||||
|
||||
XCTAssertNoThrow(try testServer.stop())
|
||||
XCTAssertNotNil(channel)
|
||||
XCTAssertNoThrow(try channel.closeFuture.wait())
|
||||
}
|
||||
|
||||
func testReceive() throws {
|
||||
let testServer = NIOHTTP1TestServer(group: self.group)
|
||||
|
||||
let responsePromise = self.group.next().makePromise(of: String.self)
|
||||
var channel: Channel!
|
||||
XCTAssertNoThrow(channel = try self.connect(serverPort: testServer.serverPort,
|
||||
responsePromise: responsePromise).wait())
|
||||
self.sendRequest(channel: channel, uri: "/uri", message: "hello")
|
||||
|
||||
let head = try assertNoThrowWithValue(try testServer.receiveHead())
|
||||
XCTAssertEqual(head.uri, "/uri")
|
||||
|
||||
let body = try assertNoThrowWithValue(try testServer.receiveBody())
|
||||
XCTAssertEqual(body, ByteBuffer(string: "hello"))
|
||||
|
||||
let trailers = try assertNoThrowWithValue(try testServer.receiveEnd())
|
||||
XCTAssertNil(trailers)
|
||||
|
||||
XCTAssertNoThrow(try testServer.stop())
|
||||
XCTAssertNotNil(channel)
|
||||
XCTAssertNoThrow(try channel.closeFuture.wait())
|
||||
}
|
||||
|
||||
func testReceiveAndVerifyWrongPart() {
|
||||
let testServer = NIOHTTP1TestServer(group: self.group)
|
||||
|
||||
let responsePromise = self.group.next().makePromise(of: String.self)
|
||||
var channel: Channel!
|
||||
XCTAssertNoThrow(channel = try self.connect(serverPort: testServer.serverPort,
|
||||
responsePromise: responsePromise).wait())
|
||||
self.sendRequest(channel: channel, uri: "/uri", message: "hello")
|
||||
|
||||
XCTAssertThrowsError(try testServer.receiveEndAndVerify()) { error in
|
||||
XCTAssert(error is NIOHTTP1TestServerError)
|
||||
}
|
||||
|
||||
XCTAssertThrowsError(try testServer.receiveHeadAndVerify()) { error in
|
||||
XCTAssert(error is NIOHTTP1TestServerError)
|
||||
}
|
||||
|
||||
XCTAssertThrowsError(try testServer.receiveBodyAndVerify()) { error in
|
||||
XCTAssert(error is NIOHTTP1TestServerError)
|
||||
}
|
||||
|
||||
XCTAssertNoThrow(try testServer.stop())
|
||||
XCTAssertNotNil(channel)
|
||||
XCTAssertNoThrow(try channel.closeFuture.wait())
|
||||
}
|
||||
}
|
||||
|
||||
private final class TestHTTPHandler: ChannelInboundHandler {
|
||||
|
@ -352,3 +427,20 @@ func assert(_ condition: @autoclosure () -> Bool,
|
|||
XCTFail(message, file: (file), line: line)
|
||||
}
|
||||
}
|
||||
|
||||
func assertNoThrowWithValue<T>(_ body: @autoclosure () throws -> T,
|
||||
defaultValue: T? = nil,
|
||||
message: String? = nil,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line) throws -> T {
|
||||
do {
|
||||
return try body()
|
||||
} catch {
|
||||
XCTFail("\(message.map { $0 + ": " } ?? "")unexpected error \(error) thrown", file: (file), line: line)
|
||||
if let defaultValue = defaultValue {
|
||||
return defaultValue
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue