refactor: Use expected<void, error> instead of optional<error> (#2565)

Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
This commit is contained in:
emrearıyürek
2025-09-15 14:54:09 +02:00
committed by GitHub
parent 26112d17f8
commit e996f2b7ab
19 changed files with 315 additions and 259 deletions

View File

@@ -143,9 +143,9 @@ public:
*
* @param response The response to send.
* @param yield The yield context.
* @return An error if the operation failed or nullopt if it succeeded.
* @return An error if the operation fails, otherwise nothing.
*/
virtual std::optional<Error>
virtual std::expected<void, Error>
send(Response response, boost::asio::yield_context yield) = 0;
/**

View File

@@ -147,9 +147,9 @@ makeConnection(
tagDecoratorFactory
);
sslConnection->setTimeout(std::chrono::seconds{10});
auto const maybeError = sslConnection->sslHandshake(yield);
if (maybeError.has_value())
return std::unexpected{fmt::format("SSL handshake error: {}", maybeError->message())};
auto const expectedSuccess = sslConnection->sslHandshake(yield);
if (not expectedSuccess.has_value())
return std::unexpected{fmt::format("SSL handshake error: {}", expectedSuccess.error().message())};
connection = std::move(sslConnection);
} else {

View File

@@ -70,8 +70,8 @@ SubscriptionContext::send(std::shared_ptr<std::string> message)
}
tasksGroup_.spawn(yield_, [this, message = std::move(message)](boost::asio::yield_context innerYield) mutable {
auto const maybeError = connection_.get().sendShared(std::move(message), innerYield);
if (maybeError.has_value() and errorHandler_(*maybeError, connection_)) {
auto const expectedSuccess = connection_.get().sendShared(std::move(message), innerYield);
if (not expectedSuccess.has_value() and errorHandler_(expectedSuccess.error(), connection_)) {
connection_.get().close(innerYield);
gotError_ = true;
}

View File

@@ -365,9 +365,9 @@ ConnectionHandler::processRequest(
auto response = handleRequest(connection, subscriptionContext, request, yield);
LOG(log_.trace()) << connection.tag() << "Sending response: " << response.message();
auto const maybeError = connection.send(std::move(response), yield);
if (maybeError.has_value()) {
return handleError(maybeError.value(), connection);
auto const expectedSuccess = connection.send(std::move(response), yield);
if (not expectedSuccess.has_value()) {
return handleError(expectedSuccess.error(), connection);
}
return std::nullopt;
}

View File

@@ -62,7 +62,7 @@ public:
virtual std::expected<ConnectionPtr, Error>
upgrade(util::TagDecoratorFactory const& tagDecoratorFactory, boost::asio::yield_context yield) = 0;
virtual std::optional<Error>
virtual std::expected<void, Error>
sendRaw(
boost::beast::http::response<boost::beast::http::string_body> response,
boost::asio::yield_context yield
@@ -123,7 +123,7 @@ public:
HttpConnection&
operator=(HttpConnection const& other) = delete;
std::optional<Error>
std::expected<void, Error>
sslHandshake(boost::asio::yield_context yield)
requires IsSslTcpStream<StreamType>
{
@@ -132,11 +132,11 @@ public:
auto const bytesUsed =
stream_.async_handshake(boost::asio::ssl::stream_base::server, buffer_.cdata(), yield[error]);
if (error)
return error;
return std::unexpected{error};
buffer_.consume(bytesUsed);
return std::nullopt;
return {};
}
bool
@@ -145,7 +145,7 @@ public:
return false;
}
std::optional<Error>
std::expected<void, Error>
sendRaw(
boost::beast::http::response<boost::beast::http::string_body> response,
boost::asio::yield_context yield
@@ -160,7 +160,7 @@ public:
timeout_ = newTimeout;
}
std::optional<Error>
std::expected<void, Error>
send(Response response, boost::asio::yield_context yield) override
{
auto httpResponse = std::move(response).intoHttpResponse();

View File

@@ -47,15 +47,15 @@ public:
{
}
std::optional<Error>
std::expected<void, Error>
send(T message, boost::asio::yield_context yield)
{
if (error_)
return error_;
return std::unexpected{error_};
queue_.push(std::move(message));
if (isSending_)
return std::nullopt;
return {};
isSending_ = true;
while (not queue_.empty() and not error_) {
@@ -65,8 +65,8 @@ public:
}
isSending_ = false;
if (error_)
return error_;
return std::nullopt;
return std::unexpected{error_};
return {};
}
};

View File

@@ -59,7 +59,7 @@ class WsConnectionBase : public Connection {
public:
using Connection::Connection;
virtual std::optional<Error>
virtual std::expected<void, Error>
sendShared(std::shared_ptr<std::string> message, boost::asio::yield_context yield) = 0;
};
@@ -108,14 +108,14 @@ public:
WsConnection&
operator=(WsConnection const&) = delete;
std::optional<Error>
std::expected<void, Error>
performHandshake(boost::asio::yield_context yield)
{
Error error;
stream_.async_accept(initialRequest_, yield[error]);
if (error)
return error;
return std::nullopt;
return std::unexpected{error};
return {};
}
bool
@@ -124,7 +124,7 @@ public:
return true;
}
std::optional<Error>
std::expected<void, Error>
sendShared(std::shared_ptr<std::string> message, boost::asio::yield_context yield) override
{
return sendingQueue_.send(std::move(message), yield);
@@ -140,7 +140,7 @@ public:
stream_.set_option(wsTimeout);
}
std::optional<Error>
std::expected<void, Error>
send(Response response, boost::asio::yield_context yield) override
{
return sendingQueue_.send(std::move(response), yield);
@@ -206,9 +206,9 @@ makeWsConnection(
auto connection = std::make_unique<WsConnection<StreamType>>(
std::forward<StreamType>(stream), std::move(ip), std::move(buffer), std::move(request), tagDecoratorFactory
);
auto maybeError = connection->performHandshake(yield);
if (maybeError.has_value())
return std::unexpected{maybeError.value()};
auto const expectedSuccess = connection->performHandshake(yield);
if (not expectedSuccess.has_value())
return std::unexpected{expectedSuccess.error()};
return connection;
}

View File

@@ -186,7 +186,7 @@ HttpAsyncClient::HttpAsyncClient(boost::asio::io_context& ioContext) : stream_{i
{
}
std::optional<boost::system::error_code>
std::expected<void, boost::system::error_code>
HttpAsyncClient::connect(
std::string_view host,
std::string_view port,
@@ -198,18 +198,18 @@ HttpAsyncClient::connect(
boost::asio::ip::tcp::resolver resolver{stream_.get_executor()};
auto const resolverResults = resolver.resolve(host, port, error);
if (error)
return error;
return std::unexpected{error};
ASSERT(!resolverResults.empty(), "No results from resolver");
boost::beast::get_lowest_layer(stream_).expires_after(timeout);
stream_.async_connect(resolverResults.begin()->endpoint(), yield[error]);
if (error)
return error;
return std::nullopt;
return std::unexpected{error};
return {};
}
std::optional<boost::system::error_code>
std::expected<void, boost::system::error_code>
HttpAsyncClient::send(
boost::beast::http::request<boost::beast::http::string_body> request,
boost::asio::yield_context yield,
@@ -221,8 +221,8 @@ HttpAsyncClient::send(
boost::beast::get_lowest_layer(stream_).expires_after(timeout);
http::async_write(stream_, request, yield[error]);
if (error)
return error;
return std::nullopt;
return std::unexpected{error};
return {};
}
std::expected<boost::beast::http::response<boost::beast::http::string_body>, boost::system::error_code>

View File

@@ -78,7 +78,7 @@ class HttpAsyncClient {
public:
HttpAsyncClient(boost::asio::io_context& ioContext);
std::optional<boost::system::error_code>
std::expected<void, boost::system::error_code>
connect(
std::string_view host,
std::string_view port,
@@ -86,7 +86,7 @@ public:
std::chrono::steady_clock::duration timeout
);
std::optional<boost::system::error_code>
std::expected<void, boost::system::error_code>
send(
boost::beast::http::request<boost::beast::http::string_body> request,
boost::asio::yield_context yield,
@@ -98,6 +98,7 @@ public:
void
gracefulShutdown();
void
disconnect();
};

View File

@@ -137,7 +137,7 @@ WebSocketAsyncClient::WebSocketAsyncClient(boost::asio::io_context& ioContext) :
{
}
std::optional<boost::system::error_code>
std::expected<void, boost::system::error_code>
WebSocketAsyncClient::connect(
std::string const& host,
std::string const& port,
@@ -153,7 +153,7 @@ WebSocketAsyncClient::connect(
boost::beast::get_lowest_layer(stream_).expires_after(timeout);
stream_.next_layer().async_connect(results, yield[error]);
if (error)
return error;
return std::unexpected{error};
boost::beast::websocket::stream_base::timeout wsTimeout =
boost::beast::websocket::stream_base::timeout::suggested(boost::beast::role_type::client);
@@ -173,12 +173,12 @@ WebSocketAsyncClient::connect(
stream_.async_handshake(fmt::format("{}:{}", host, port), "/", yield[error]);
if (error)
return error;
return std::unexpected{error};
return std::nullopt;
return {};
}
std::optional<boost::system::error_code>
std::expected<void, boost::system::error_code>
WebSocketAsyncClient::send(
boost::asio::yield_context yield,
std::string_view message,
@@ -190,8 +190,8 @@ WebSocketAsyncClient::send(
);
if (error)
return error;
return std::nullopt;
return std::unexpected{error};
return {};
}
std::expected<std::string, boost::system::error_code>

View File

@@ -56,7 +56,7 @@ class WebSocketAsyncClient {
public:
WebSocketAsyncClient(boost::asio::io_context& ioContext);
std::optional<boost::system::error_code>
std::expected<void, boost::system::error_code>
connect(
std::string const& host,
std::string const& port,
@@ -65,7 +65,7 @@ public:
std::vector<WebHeader> additionalHeaders = {}
);
std::optional<boost::system::error_code>
std::expected<void, boost::system::error_code>
send(boost::asio::yield_context yield, std::string_view message, std::chrono::steady_clock::duration timeout);
std::expected<std::string, boost::system::error_code>

View File

@@ -48,7 +48,7 @@ struct MockConnectionImpl : web::ng::Connection {
MOCK_METHOD(void, setTimeout, (std::chrono::steady_clock::duration), (override));
using SendReturnType = std::optional<web::ng::Error>;
using SendReturnType = std::expected<void, web::ng::Error>;
MOCK_METHOD(SendReturnType, send, (web::ng::Response, boost::asio::yield_context), (override));
using ReceiveReturnType = std::expected<web::ng::Request, web::ng::Error>;

View File

@@ -44,7 +44,7 @@ struct MockHttpConnectionImpl : web::ng::impl::UpgradableConnection {
MOCK_METHOD(void, setTimeout, (std::chrono::steady_clock::duration), (override));
using SendReturnType = std::optional<web::ng::Error>;
using SendReturnType = std::expected<void, web::ng::Error>;
MOCK_METHOD(SendReturnType, send, (web::ng::Response, boost::asio::yield_context), (override));
MOCK_METHOD(

View File

@@ -42,7 +42,7 @@ struct MockWsConnectionImpl : web::ng::impl::WsConnectionBase {
MOCK_METHOD(void, setTimeout, (std::chrono::steady_clock::duration), (override));
using SendReturnType = std::optional<web::ng::Error>;
using SendReturnType = std::expected<void, web::ng::Error>;
MOCK_METHOD(SendReturnType, send, (web::ng::Response, boost::asio::yield_context), (override));
using ReceiveReturnType = std::expected<web::ng::Request, web::ng::Error>;
@@ -50,13 +50,7 @@ struct MockWsConnectionImpl : web::ng::impl::WsConnectionBase {
MOCK_METHOD(void, close, (boost::asio::yield_context), (override));
using SendBufferReturnType = std::optional<web::ng::Error>;
MOCK_METHOD(
SendBufferReturnType,
sendShared,
(std::shared_ptr<std::string>, boost::asio::yield_context),
(override)
);
MOCK_METHOD(SendReturnType, sendShared, (std::shared_ptr<std::string>, boost::asio::yield_context), (override));
};
using MockWsConnection = testing::NiceMock<MockWsConnectionImpl>;

View File

@@ -261,9 +261,9 @@ TEST_F(ServerHttpTest, ClientDisconnects)
{
HttpAsyncClient client{ctx_};
util::spawn(ctx_, [&](boost::asio::yield_context yield) {
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort_), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
client.disconnect();
server_->stop(yield);
@@ -312,16 +312,17 @@ TEST_F(ServerHttpTest, OnConnectCheck)
return {};
});
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
// Have to send a request here because the server does async_detect_ssl() which waits for some data to appear
client.send(
expectedSuccess = client.send(
http::request<http::string_body>{http::verb::get, "/", 11, requestMessage_},
yield,
std::chrono::milliseconds{100}
);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
// Wait for the onConnectCheck to be called
timer.expires_after(std::chrono::milliseconds{100});
@@ -374,16 +375,17 @@ TEST_F(ServerHttpTest, OnConnectCheckFailed)
});
util::spawn(ctx_, [&](boost::asio::yield_context yield) {
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
// Have to send a request here because the server does async_detect_ssl() which waits for some data to appear
client.send(
expectedSuccess = client.send(
http::request<http::string_body>{http::verb::get, "/", 11, requestMessage_},
yield,
std::chrono::milliseconds{100}
);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto const response = client.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(response.has_value()) << response.error().message(); }();
@@ -434,15 +436,17 @@ TEST_F(ServerHttpTest, OnDisconnectHook)
EXPECT_CALL(onDisconnectHookMock, Call).WillOnce([&timer](auto&&) { timer.cancel(); });
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
client.send(
// Have to send a request here because the server does async_detect_ssl() which waits for some data to appear
expectedSuccess = client.send(
http::request<http::string_body>{http::verb::get, "/", 11, requestMessage_},
yield,
std::chrono::milliseconds{100}
);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
client.gracefulShutdown();
@@ -463,17 +467,17 @@ TEST_F(ServerHttpTest, ClientIsDisconnectedIfServerStopped)
{
HttpAsyncClient client{ctx_};
util::spawn(ctx_, [&](boost::asio::yield_context yield) {
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort_), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
// Have to send a request here because the server does async_detect_ssl() which waits for some data to appear
maybeError = client.send(
expectedSuccess = client.send(
http::request<http::string_body>{http::verb::get, "/", 11, requestMessage_},
yield,
std::chrono::milliseconds{100}
);
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto message = client.receive(yield, std::chrono::milliseconds{100});
EXPECT_TRUE(message.has_value()) << message.error().message();
@@ -498,13 +502,13 @@ TEST_P(ServerHttpTest, RequestResponse)
Response const response{http::status::ok, "some response", Request{request}};
util::spawn(ctx_, [&](boost::asio::yield_context yield) {
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort_), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
maybeError = client.send(request, yield, std::chrono::milliseconds{100});
EXPECT_FALSE(maybeError.has_value()) << maybeError->message();
expectedSuccess = client.send(request, yield, std::chrono::milliseconds{100});
EXPECT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message();
auto const expectedResponse = client.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
@@ -548,9 +552,9 @@ TEST_F(ServerTest, WsClientDisconnects)
WebSocketAsyncClient client{ctx_};
util::spawn(ctx_, [&](boost::asio::yield_context yield) {
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort_), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
client.close();
server_->stop(yield);
@@ -570,13 +574,13 @@ TEST_F(ServerTest, WsRequestResponse)
Response const response{http::status::ok, "some response", Request{requestMessage_, headers}};
util::spawn(ctx_, [&](boost::asio::yield_context yield) {
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort_), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
maybeError = client.send(yield, requestMessage_, std::chrono::milliseconds{100});
EXPECT_FALSE(maybeError.has_value()) << maybeError->message();
expectedSuccess = client.send(yield, requestMessage_, std::chrono::milliseconds{100});
EXPECT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message();
auto const expectedResponse = client.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
@@ -608,10 +612,10 @@ TEST_F(ServerTest, WsClientIsDisconnectedIfServerStopped)
{
WebSocketAsyncClient client{ctx_};
util::spawn(ctx_, [&](boost::asio::yield_context yield) {
auto maybeError =
auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort_), yield, std::chrono::milliseconds{100});
EXPECT_TRUE(maybeError.has_value());
EXPECT_EQ(maybeError.value().value(), static_cast<int>(boost::beast::websocket::error::upgrade_declined));
EXPECT_FALSE(expectedSuccess.has_value());
EXPECT_EQ(expectedSuccess.error().value(), static_cast<int>(boost::beast::websocket::error::upgrade_declined));
ctx_.stop();
});

View File

@@ -65,10 +65,13 @@ TEST_F(NgSubscriptionContextTests, Send)
auto subscriptionContext = makeSubscriptionContext(yield);
auto const message = std::make_shared<std::string>("some message");
EXPECT_CALL(connection_, sendShared).WillOnce([&message](std::shared_ptr<std::string> sendingMessage, auto&&) {
EXPECT_CALL(connection_, sendShared)
.WillOnce(
[&message](std::shared_ptr<std::string> sendingMessage, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(sendingMessage, message);
return std::nullopt;
});
return {};
}
);
subscriptionContext.send(message);
subscriptionContext.disconnect(yield);
});
@@ -84,16 +87,24 @@ TEST_F(NgSubscriptionContextTests, SendOrder)
testing::Sequence const sequence;
EXPECT_CALL(connection_, sendShared)
.InSequence(sequence)
.WillOnce([&message1](std::shared_ptr<std::string> sendingMessage, auto&&) {
.WillOnce(
[&message1](
std::shared_ptr<std::string> sendingMessage, auto&&
) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(sendingMessage, message1);
return std::nullopt;
});
return {};
}
);
EXPECT_CALL(connection_, sendShared)
.InSequence(sequence)
.WillOnce([&message2](std::shared_ptr<std::string> sendingMessage, auto&&) {
.WillOnce(
[&message2](
std::shared_ptr<std::string> sendingMessage, auto&&
) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(sendingMessage, message2);
return std::nullopt;
});
return {};
}
);
subscriptionContext.send(message1);
subscriptionContext.send(message2);
@@ -109,7 +120,7 @@ TEST_F(NgSubscriptionContextTests, SendFailed)
EXPECT_CALL(connection_, sendShared).WillOnce([&message](std::shared_ptr<std::string> sendingMessage, auto&&) {
EXPECT_EQ(sendingMessage, message);
return boost::system::errc::make_error_code(boost::system::errc::not_supported);
return std::unexpected{boost::system::errc::make_error_code(boost::system::errc::not_supported)};
});
EXPECT_CALL(errorHandler_, Call).WillOnce(testing::Return(true));
EXPECT_CALL(connection_, close);
@@ -125,11 +136,15 @@ TEST_F(NgSubscriptionContextTests, SendTooManySubscriptions)
auto const message = std::make_shared<std::string>("message1");
EXPECT_CALL(connection_, sendShared)
.WillOnce([&message](std::shared_ptr<std::string> sendingMessage, boost::asio::yield_context innerYield) {
.WillOnce(
[&message](
std::shared_ptr<std::string> sendingMessage, boost::asio::yield_context innerYield
) -> std::expected<void, web::ng::Error> {
boost::asio::post(innerYield); // simulate send is slow by switching to another coroutine
EXPECT_EQ(sendingMessage, message);
return std::nullopt;
});
return {};
}
);
EXPECT_CALL(connection_, close);
subscriptionContext.send(message);

View File

@@ -173,9 +173,10 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_NoHandler_Send)
.WillOnce(Return(makeRequest("some_request", headers)))
.WillOnce(Return(makeError(websocket::error::closed)));
EXPECT_CALL(*mockHttpConnection, send).WillOnce([](Response response, auto&&) {
EXPECT_CALL(*mockHttpConnection, send)
.WillOnce([](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), "WebSocket is not supported by this server");
return std::nullopt;
return {};
});
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockHttpConnection.get()](Connection const& c) {
@@ -197,12 +198,13 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_BadTarget_Send)
.WillOnce(Return(makeRequest(http::request<http::string_body>{http::verb::get, target, 11, requestMessage})))
.WillOnce(Return(makeError(http::error::end_of_stream)));
EXPECT_CALL(*mockHttpConnection, send).WillOnce([](Response response, auto&&) {
EXPECT_CALL(*mockHttpConnection, send)
.WillOnce([](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), "Bad target");
auto const httpResponse = std::move(response).intoHttpResponse();
EXPECT_EQ(httpResponse.result(), http::status::bad_request);
EXPECT_EQ(httpResponse.version(), 11);
return std::nullopt;
return {};
});
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockHttpConnection.get()](Connection const& c) {
@@ -221,9 +223,10 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_BadMethod_Send)
.WillOnce(Return(makeRequest(http::request<http::string_body>{http::verb::acl, "/", 11})))
.WillOnce(Return(makeError(http::error::end_of_stream)));
EXPECT_CALL(*mockHttpConnection, send).WillOnce([](Response response, auto&&) {
EXPECT_CALL(*mockHttpConnection, send)
.WillOnce([](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), "Unsupported http method");
return std::nullopt;
return {};
});
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockHttpConnection.get()](Connection const& c) {
@@ -255,9 +258,10 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_Send)
return Response(http::status::ok, responseMessage, request);
});
EXPECT_CALL(*mockWsConnection, send).WillOnce([&responseMessage](Response response, auto&&) {
EXPECT_CALL(*mockWsConnection, send)
.WillOnce([&responseMessage](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), responseMessage);
return std::nullopt;
return {};
});
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) {
@@ -290,13 +294,17 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, SendSubscriptionMessage)
return Response(http::status::ok, "", request);
});
EXPECT_CALL(*mockWsConnection, send).WillOnce(Return(std::nullopt));
EXPECT_CALL(*mockWsConnection, send).WillOnce(Return(std::expected<void, web::ng::Error>{}));
EXPECT_CALL(*mockWsConnection, sendShared)
.WillOnce([&subscriptionMessage](std::shared_ptr<std::string> sendingMessage, auto&&) {
.WillOnce(
[&subscriptionMessage](
std::shared_ptr<std::string> sendingMessage, auto&&
) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(*sendingMessage, subscriptionMessage);
return std::nullopt;
});
return {};
}
);
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) {
EXPECT_EQ(&c, connectionPtr);
@@ -328,7 +336,7 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, SubscriptionContextIsDisconnec
return Response(http::status::ok, "", request);
});
EXPECT_CALL(*mockWsConnection, send).WillOnce(Return(std::nullopt));
EXPECT_CALL(*mockWsConnection, send).WillOnce(Return(std::expected<void, web::ng::Error>{}));
EXPECT_CALL(onDisconnectHook, Call).After(expectationReceiveCalled);
@@ -367,9 +375,10 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, SubscriptionContextIsNullForHt
return Response(http::status::ok, responseMessage, request);
});
EXPECT_CALL(*mockHttpConnection, send).WillOnce([&responseMessage](Response response, auto&&) {
EXPECT_CALL(*mockHttpConnection, send)
.WillOnce([&responseMessage](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), responseMessage);
return std::nullopt;
return {};
});
EXPECT_CALL(
@@ -413,9 +422,11 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_Send_Loop)
return Response(http::status::ok, responseMessage, request);
});
EXPECT_CALL(*mockHttpConnection, send).Times(3).WillRepeatedly([&responseMessage](Response response, auto&&) {
EXPECT_CALL(*mockHttpConnection, send)
.Times(3)
.WillRepeatedly([&responseMessage](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), responseMessage);
return std::nullopt;
return {};
});
EXPECT_CALL(
@@ -456,7 +467,7 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_SendError)
EXPECT_CALL(*mockHttpConnection, send).WillOnce([&responseMessage](Response response, auto&&) {
EXPECT_EQ(response.message(), responseMessage);
return makeError(http::error::end_of_stream).error();
return makeError(http::error::end_of_stream);
});
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockHttpConnection.get()](Connection const& c) {
@@ -498,7 +509,7 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, OnIpChangeHookCalledWhenSentFr
EXPECT_CALL(*mockHttpConnectionFromProxy, send).WillOnce([&responseMessage](Response response, auto&&) {
EXPECT_EQ(response.message(), responseMessage);
return makeError(http::error::end_of_stream).error();
return makeError(http::error::end_of_stream);
});
EXPECT_CALL(onDisconnectMock, Call)
@@ -542,12 +553,12 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Stop)
send(testing::ResultOf([](Response const& r) { return r.message(); }, responseMessage), testing::_)
)
.Times(3)
.WillRepeatedly([&](auto&&, auto&&) {
.WillRepeatedly([&](auto&&, auto&&) -> std::expected<void, web::ng::Error> {
++numCalls;
if (numCalls == 3)
util::spawn(ctx_, [this](auto yield) { connectionHandler.stop(yield); });
return std::nullopt;
return {};
});
EXPECT_CALL(
@@ -659,9 +670,10 @@ TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send)
return Response(http::status::ok, responseMessage, request);
});
EXPECT_CALL(*mockWsConnection, send).WillOnce([&responseMessage](Response response, auto&&) {
EXPECT_CALL(*mockWsConnection, send)
.WillOnce([&responseMessage](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), responseMessage);
return std::nullopt;
return {};
});
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) {
@@ -699,9 +711,10 @@ TEST_F(ConnectionHandlerParallelProcessingTest, OnIpChangeHookCalledWhenSentFrom
return Response(http::status::ok, responseMessage, request);
});
EXPECT_CALL(*mockWsConnectionFromProxy, send).WillOnce([&responseMessage](Response response, auto&&) {
EXPECT_CALL(*mockWsConnectionFromProxy, send)
.WillOnce([&responseMessage](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), responseMessage);
return std::nullopt;
return {};
});
EXPECT_CALL(onDisconnectMock, Call)
@@ -738,9 +751,11 @@ TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send_Loop)
return Response(http::status::ok, responseMessage, request);
});
EXPECT_CALL(*mockWsConnection, send).Times(2).WillRepeatedly([&responseMessage](Response response, auto&&) {
EXPECT_CALL(*mockWsConnection, send)
.Times(2)
.WillRepeatedly([&responseMessage](Response response, auto&&) -> std::expected<void, web::ng::Error> {
EXPECT_EQ(response.message(), responseMessage);
return std::nullopt;
return {};
});
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) {
@@ -787,7 +802,7 @@ TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send_Loop_TooMany
send(testing::ResultOf([](Response response) { return response.message(); }, responseMessage), testing::_)
)
.Times(3)
.WillRepeatedly(Return(std::nullopt));
.WillRepeatedly(Return(std::expected<void, web::ng::Error>{}));
EXPECT_CALL(
*mockWsConnection,
@@ -799,7 +814,7 @@ TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send_Loop_TooMany
)
)
.Times(2)
.WillRepeatedly(Return(std::nullopt));
.WillRepeatedly(Return(std::expected<void, web::ng::Error>{}));
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) {
EXPECT_EQ(&c, connectionPtr);

View File

@@ -80,8 +80,9 @@ protected:
TEST_F(HttpConnectionTests, wasUpgraded)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -95,11 +96,12 @@ TEST_F(HttpConnectionTests, Receive)
request_.set(boost::beast::http::field::user_agent, "test_client");
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
maybeError = httpClient_.send(request_, yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
expectedSuccess = httpClient_.send(request_, yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -123,8 +125,9 @@ TEST_F(HttpConnectionTests, Receive)
TEST_F(HttpConnectionTests, ReceiveTimeout)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{1});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{1});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -138,8 +141,9 @@ TEST_F(HttpConnectionTests, ReceiveTimeout)
TEST_F(HttpConnectionTests, ReceiveClientDisconnected)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{1});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{1});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
httpClient_.disconnect();
});
@@ -157,11 +161,12 @@ TEST_F(HttpConnectionTests, Send)
Response const response{http::status::ok, "some response data", request};
util::spawn(ctx_, [this, response = response](boost::asio::yield_context yield) mutable {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto const expectedResponse = httpClient_.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
auto const receivedResponse = expectedResponse.value();
auto const sentResponse = std::move(response).intoHttpResponse();
@@ -173,8 +178,8 @@ TEST_F(HttpConnectionTests, Send)
runSpawn([this, &response](boost::asio::yield_context yield) {
auto connection = acceptConnection(yield);
auto maybeError = connection->send(response, yield);
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess = connection->send(response, yield);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
}
@@ -184,8 +189,9 @@ TEST_F(HttpConnectionTests, SendMultipleTimes)
Response const response{http::status::ok, "some response data", request};
util::spawn(ctx_, [this, response = response](boost::asio::yield_context yield) mutable {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
auto const expectedResponse = httpClient_.receive(yield, std::chrono::milliseconds{100});
@@ -204,8 +210,8 @@ TEST_F(HttpConnectionTests, SendMultipleTimes)
auto connection = acceptConnection(yield);
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
auto maybeError = connection->send(response, yield);
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess = connection->send(response, yield);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
}
});
}
@@ -216,9 +222,9 @@ TEST_F(HttpConnectionTests, SendMultipleTimesFromMultipleCoroutines)
Response const response{http::status::ok, "some response data", request};
util::spawn(ctx_, [this, response = response](boost::asio::yield_context yield) mutable {
auto const maybeError =
auto const expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
auto const expectedResponse = httpClient_.receive(yield, std::chrono::milliseconds{100});
@@ -239,8 +245,8 @@ TEST_F(HttpConnectionTests, SendMultipleTimesFromMultipleCoroutines)
util::CoroutineGroup group{yield};
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
group.spawn(yield, [&response, &connection](boost::asio::yield_context innerYield) {
auto const maybeError = connection->send(response, innerYield);
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto const expectedSuccess = connection->send(response, innerYield);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
}
group.asyncWait(yield);
@@ -251,9 +257,9 @@ TEST_F(HttpConnectionTests, SendMultipleTimesClientDisconnected)
{
Response const response{http::status::ok, "some response data", Request{request_}};
util::spawn(ctx_, [this, response = response](boost::asio::yield_context yield) mutable {
auto const maybeError =
auto const expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{1});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto const expectedResponse = httpClient_.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
httpClient_.disconnect();
@@ -262,15 +268,15 @@ TEST_F(HttpConnectionTests, SendMultipleTimesClientDisconnected)
runSpawn([this, &response](boost::asio::yield_context yield) {
auto connection = acceptConnection(yield);
connection->setTimeout(std::chrono::milliseconds{1});
auto maybeError = connection->send(response, yield);
auto expectedSuccess = connection->send(response, yield);
size_t counter{1};
while (not maybeError.has_value() and counter < 100) {
while (expectedSuccess.has_value() and counter < 100) {
++counter;
maybeError = connection->send(response, yield);
expectedSuccess = connection->send(response, yield);
}
// Sending after getting an error should be safe
maybeError = connection->send(response, yield);
EXPECT_TRUE(maybeError.has_value());
expectedSuccess = connection->send(response, yield);
EXPECT_FALSE(expectedSuccess.has_value());
EXPECT_LT(counter, 100);
});
}
@@ -279,20 +285,21 @@ TEST_F(HttpConnectionTests, SendClientDisconnected)
{
Response const response{http::status::ok, "some response data", Request{request_}};
util::spawn(ctx_, [this, response = response](boost::asio::yield_context yield) mutable {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{1});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{1});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
httpClient_.disconnect();
});
runSpawn([this, &response](boost::asio::yield_context yield) {
auto connection = acceptConnection(yield);
connection->setTimeout(std::chrono::milliseconds{1});
auto maybeError = connection->send(response, yield);
auto expectedSuccess = connection->send(response, yield);
size_t counter{1};
while (not maybeError.has_value() and counter < 100) {
while (expectedSuccess.has_value() and counter < 100) {
++counter;
maybeError = connection->send(response, yield);
expectedSuccess = connection->send(response, yield);
}
EXPECT_TRUE(maybeError.has_value());
EXPECT_FALSE(expectedSuccess.has_value());
EXPECT_LT(counter, 100);
});
}
@@ -300,15 +307,16 @@ TEST_F(HttpConnectionTests, SendClientDisconnected)
TEST_F(HttpConnectionTests, Close)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
size_t counter{0};
while (not maybeError.has_value() and counter < 100) {
while (expectedSuccess.has_value() and counter < 100) {
++counter;
maybeError = httpClient_.send(request_, yield, std::chrono::milliseconds{1});
expectedSuccess = httpClient_.send(request_, yield, std::chrono::milliseconds{1});
}
EXPECT_TRUE(maybeError.has_value());
EXPECT_FALSE(expectedSuccess.has_value());
EXPECT_LT(counter, 100);
});
@@ -322,11 +330,12 @@ TEST_F(HttpConnectionTests, Close)
TEST_F(HttpConnectionTests, IsUpgradeRequested_GotHttpRequest)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
maybeError = httpClient_.send(request_, yield, std::chrono::milliseconds{1});
EXPECT_FALSE(maybeError.has_value()) << maybeError->message();
expectedSuccess = httpClient_.send(request_, yield, std::chrono::milliseconds{1});
EXPECT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -340,8 +349,9 @@ TEST_F(HttpConnectionTests, IsUpgradeRequested_GotHttpRequest)
TEST_F(HttpConnectionTests, IsUpgradeRequested_FailedToFetch)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -357,8 +367,8 @@ TEST_F(HttpConnectionTests, Upgrade)
WebSocketAsyncClient wsClient{ctx_};
util::spawn(ctx_, [this, &wsClient](boost::asio::yield_context yield) {
auto maybeError = wsClient.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess = wsClient.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -375,8 +385,9 @@ TEST_F(HttpConnectionTests, Upgrade)
TEST_F(HttpConnectionTests, Ip)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) mutable {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -391,8 +402,9 @@ TEST_F(HttpConnectionTests, isAdminSetAdmin)
EXPECT_CALL(adminSetter, Call).WillOnce(testing::Return(true));
util::spawn(ctx_, [this](boost::asio::yield_context yield) mutable {
auto maybeError = httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError->message(); }();
auto expectedSuccess =
httpClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([&](boost::asio::yield_context yield) {

View File

@@ -99,8 +99,9 @@ protected:
TEST_F(WebWsConnectionTests, WasUpgraded)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([this](boost::asio::yield_context yield) {
auto wsConnection = acceptConnection(yield);
@@ -115,8 +116,9 @@ TEST_F(WebWsConnectionTests, DisconnectClientOnInactivity)
std::thread clientThread{[&clientCtx]() { clientCtx.run(); }};
util::spawn(clientCtx, [&work, this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
boost::asio::steady_timer timer{yield.get_executor(), std::chrono::milliseconds{5}};
timer.async_wait(yield);
work.reset();
@@ -143,8 +145,9 @@ TEST_F(WebWsConnectionTests, Send)
Response const response{boost::beast::http::status::ok, "some response", request_};
util::spawn(ctx_, [this, &response](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto const expectedMessage = wsClient_.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedMessage.has_value()) << expectedMessage.error().message(); }();
EXPECT_EQ(expectedMessage.value(), response.message());
@@ -152,8 +155,8 @@ TEST_F(WebWsConnectionTests, Send)
runSpawn([this, &response](boost::asio::yield_context yield) {
auto wsConnection = acceptConnection(yield);
auto maybeError = wsConnection->send(response, yield);
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess = wsConnection->send(response, yield);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
}
@@ -162,8 +165,9 @@ TEST_F(WebWsConnectionTests, SendShared)
auto const response = std::make_shared<std::string>("some response");
util::spawn(ctx_, [this, &response](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto const expectedMessage = wsClient_.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedMessage.has_value()) << expectedMessage.error().message(); }();
EXPECT_EQ(expectedMessage.value(), *response);
@@ -171,8 +175,8 @@ TEST_F(WebWsConnectionTests, SendShared)
runSpawn([this, &response](boost::asio::yield_context yield) {
auto wsConnection = acceptConnection(yield);
auto maybeError = wsConnection->sendShared(response, yield);
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess = wsConnection->sendShared(response, yield);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
}
@@ -181,8 +185,9 @@ TEST_F(WebWsConnectionTests, MultipleSend)
Response const response{boost::beast::http::status::ok, "some response", request_};
util::spawn(ctx_, [this, &response](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
auto const expectedMessage = wsClient_.receive(yield, std::chrono::milliseconds{100});
@@ -195,8 +200,8 @@ TEST_F(WebWsConnectionTests, MultipleSend)
auto wsConnection = acceptConnection(yield);
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
auto maybeError = wsConnection->send(response, yield);
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess = wsConnection->send(response, yield);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
}
});
}
@@ -206,8 +211,9 @@ TEST_F(WebWsConnectionTests, MultipleSendFromMultipleCoroutines)
Response const response{boost::beast::http::status::ok, "some response", request_};
util::spawn(ctx_, [this, &response](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
auto const expectedMessage = wsClient_.receive(yield, std::chrono::milliseconds{100});
@@ -222,8 +228,8 @@ TEST_F(WebWsConnectionTests, MultipleSendFromMultipleCoroutines)
util::CoroutineGroup group{yield};
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
group.spawn(yield, [&wsConnection, &response](boost::asio::yield_context innerYield) {
auto maybeError = wsConnection->send(response, innerYield);
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess = wsConnection->send(response, innerYield);
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
}
group.asyncWait(yield);
@@ -235,21 +241,22 @@ TEST_F(WebWsConnectionTests, SendFailed)
Response const response{boost::beast::http::status::ok, "some response", request_};
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
wsClient_.close();
});
runSpawn([this, &response](boost::asio::yield_context yield) {
auto wsConnection = acceptConnection(yield);
wsConnection->setTimeout(std::chrono::milliseconds{1});
std::optional<Error> maybeError;
std::expected<void, Error> expectedSuccess;
size_t counter = 0;
while (not maybeError.has_value() and counter < 100) {
maybeError = wsConnection->send(response, yield);
while (expectedSuccess.has_value() and counter < 100) {
expectedSuccess = wsConnection->send(response, yield);
++counter;
}
EXPECT_TRUE(maybeError.has_value());
EXPECT_FALSE(expectedSuccess.has_value());
EXPECT_LT(counter, 100);
});
}
@@ -259,8 +266,9 @@ TEST_F(WebWsConnectionTests, SendFailedSendingFromMultipleCoroutines)
Response const response{boost::beast::http::status::ok, "some response", request_};
util::spawn(ctx_, [this, &response](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto const expectedMessage = wsClient_.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedMessage.has_value()) << expectedMessage.error().message(); }();
@@ -271,15 +279,15 @@ TEST_F(WebWsConnectionTests, SendFailedSendingFromMultipleCoroutines)
runSpawn([this, &response](boost::asio::yield_context yield) {
auto wsConnection = acceptConnection(yield);
wsConnection->setTimeout(std::chrono::milliseconds{1});
std::optional<Error> maybeError;
std::expected<void, Error> expectedSuccess;
size_t counter = 0;
while (not maybeError.has_value() and counter < 100) {
maybeError = wsConnection->send(response, yield);
while (expectedSuccess.has_value() and counter < 100) {
expectedSuccess = wsConnection->send(response, yield);
++counter;
}
// Sending after getting an error should be safe
maybeError = wsConnection->send(response, yield);
EXPECT_TRUE(maybeError.has_value());
expectedSuccess = wsConnection->send(response, yield);
EXPECT_FALSE(expectedSuccess.has_value());
EXPECT_LT(counter, 100);
});
}
@@ -287,11 +295,12 @@ TEST_F(WebWsConnectionTests, SendFailedSendingFromMultipleCoroutines)
TEST_F(WebWsConnectionTests, Receive)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
maybeError = wsClient_.send(yield, request_.message(), std::chrono::milliseconds{100});
EXPECT_FALSE(maybeError.has_value()) << maybeError->message();
expectedSuccess = wsClient_.send(yield, request_.message(), std::chrono::milliseconds{100});
EXPECT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -306,12 +315,13 @@ TEST_F(WebWsConnectionTests, Receive)
TEST_F(WebWsConnectionTests, MultipleReceive)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
maybeError = wsClient_.send(yield, request_.message(), std::chrono::milliseconds{100});
EXPECT_FALSE(maybeError.has_value()) << maybeError->message();
expectedSuccess = wsClient_.send(yield, request_.message(), std::chrono::milliseconds{100});
EXPECT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message();
}
});
@@ -329,8 +339,9 @@ TEST_F(WebWsConnectionTests, MultipleReceive)
TEST_F(WebWsConnectionTests, ReceiveTimeout)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
runSpawn([this](boost::asio::yield_context yield) {
@@ -345,8 +356,9 @@ TEST_F(WebWsConnectionTests, ReceiveTimeout)
TEST_F(WebWsConnectionTests, ReceiveFailed)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
wsClient_.close();
});
@@ -361,8 +373,9 @@ TEST_F(WebWsConnectionTests, ReceiveFailed)
TEST_F(WebWsConnectionTests, Close)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto const maybeMessage = wsClient_.receive(yield, std::chrono::milliseconds{100});
EXPECT_FALSE(maybeMessage.has_value());
EXPECT_THAT(maybeMessage.error().message(), testing::HasSubstr("was gracefully closed"));
@@ -377,8 +390,9 @@ TEST_F(WebWsConnectionTests, Close)
TEST_F(WebWsConnectionTests, CloseWhenConnectionIsAlreadyClosed)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
wsClient_.close();
});
@@ -393,8 +407,9 @@ TEST_F(WebWsConnectionTests, CloseWhenConnectionIsAlreadyClosed)
TEST_F(WebWsConnectionTests, CloseCalledFromMultipleSubCoroutines)
{
util::spawn(ctx_, [this](boost::asio::yield_context yield) {
auto maybeError = wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_FALSE(maybeError.has_value()) << maybeError.value().message(); }();
auto expectedSuccess =
wsClient_.connect("localhost", httpServer_.port(), yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
});
testing::StrictMock<testing::MockFunction<void()>> closeCalled;