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 response The response to send.
* @param yield The yield context. * @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; send(Response response, boost::asio::yield_context yield) = 0;
/** /**

View File

@@ -147,9 +147,9 @@ makeConnection(
tagDecoratorFactory tagDecoratorFactory
); );
sslConnection->setTimeout(std::chrono::seconds{10}); sslConnection->setTimeout(std::chrono::seconds{10});
auto const maybeError = sslConnection->sslHandshake(yield); auto const expectedSuccess = sslConnection->sslHandshake(yield);
if (maybeError.has_value()) if (not expectedSuccess.has_value())
return std::unexpected{fmt::format("SSL handshake error: {}", maybeError->message())}; return std::unexpected{fmt::format("SSL handshake error: {}", expectedSuccess.error().message())};
connection = std::move(sslConnection); connection = std::move(sslConnection);
} else { } 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 { tasksGroup_.spawn(yield_, [this, message = std::move(message)](boost::asio::yield_context innerYield) mutable {
auto const maybeError = connection_.get().sendShared(std::move(message), innerYield); auto const expectedSuccess = connection_.get().sendShared(std::move(message), innerYield);
if (maybeError.has_value() and errorHandler_(*maybeError, connection_)) { if (not expectedSuccess.has_value() and errorHandler_(expectedSuccess.error(), connection_)) {
connection_.get().close(innerYield); connection_.get().close(innerYield);
gotError_ = true; gotError_ = true;
} }

View File

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

View File

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

View File

@@ -59,7 +59,7 @@ class WsConnectionBase : public Connection {
public: public:
using Connection::Connection; 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; sendShared(std::shared_ptr<std::string> message, boost::asio::yield_context yield) = 0;
}; };
@@ -108,14 +108,14 @@ public:
WsConnection& WsConnection&
operator=(WsConnection const&) = delete; operator=(WsConnection const&) = delete;
std::optional<Error> std::expected<void, Error>
performHandshake(boost::asio::yield_context yield) performHandshake(boost::asio::yield_context yield)
{ {
Error error; Error error;
stream_.async_accept(initialRequest_, yield[error]); stream_.async_accept(initialRequest_, yield[error]);
if (error) if (error)
return error; return std::unexpected{error};
return std::nullopt; return {};
} }
bool bool
@@ -124,7 +124,7 @@ public:
return true; return true;
} }
std::optional<Error> std::expected<void, Error>
sendShared(std::shared_ptr<std::string> message, boost::asio::yield_context yield) override sendShared(std::shared_ptr<std::string> message, boost::asio::yield_context yield) override
{ {
return sendingQueue_.send(std::move(message), yield); return sendingQueue_.send(std::move(message), yield);
@@ -140,7 +140,7 @@ public:
stream_.set_option(wsTimeout); stream_.set_option(wsTimeout);
} }
std::optional<Error> std::expected<void, Error>
send(Response response, boost::asio::yield_context yield) override send(Response response, boost::asio::yield_context yield) override
{ {
return sendingQueue_.send(std::move(response), yield); return sendingQueue_.send(std::move(response), yield);
@@ -206,9 +206,9 @@ makeWsConnection(
auto connection = std::make_unique<WsConnection<StreamType>>( auto connection = std::make_unique<WsConnection<StreamType>>(
std::forward<StreamType>(stream), std::move(ip), std::move(buffer), std::move(request), tagDecoratorFactory std::forward<StreamType>(stream), std::move(ip), std::move(buffer), std::move(request), tagDecoratorFactory
); );
auto maybeError = connection->performHandshake(yield); auto const expectedSuccess = connection->performHandshake(yield);
if (maybeError.has_value()) if (not expectedSuccess.has_value())
return std::unexpected{maybeError.value()}; return std::unexpected{expectedSuccess.error()};
return connection; 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( HttpAsyncClient::connect(
std::string_view host, std::string_view host,
std::string_view port, std::string_view port,
@@ -198,18 +198,18 @@ HttpAsyncClient::connect(
boost::asio::ip::tcp::resolver resolver{stream_.get_executor()}; boost::asio::ip::tcp::resolver resolver{stream_.get_executor()};
auto const resolverResults = resolver.resolve(host, port, error); auto const resolverResults = resolver.resolve(host, port, error);
if (error) if (error)
return error; return std::unexpected{error};
ASSERT(!resolverResults.empty(), "No results from resolver"); ASSERT(!resolverResults.empty(), "No results from resolver");
boost::beast::get_lowest_layer(stream_).expires_after(timeout); boost::beast::get_lowest_layer(stream_).expires_after(timeout);
stream_.async_connect(resolverResults.begin()->endpoint(), yield[error]); stream_.async_connect(resolverResults.begin()->endpoint(), yield[error]);
if (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>
HttpAsyncClient::send( HttpAsyncClient::send(
boost::beast::http::request<boost::beast::http::string_body> request, boost::beast::http::request<boost::beast::http::string_body> request,
boost::asio::yield_context yield, boost::asio::yield_context yield,
@@ -221,8 +221,8 @@ HttpAsyncClient::send(
boost::beast::get_lowest_layer(stream_).expires_after(timeout); boost::beast::get_lowest_layer(stream_).expires_after(timeout);
http::async_write(stream_, request, yield[error]); http::async_write(stream_, request, yield[error]);
if (error) if (error)
return error; return std::unexpected{error};
return std::nullopt; return {};
} }
std::expected<boost::beast::http::response<boost::beast::http::string_body>, boost::system::error_code> std::expected<boost::beast::http::response<boost::beast::http::string_body>, boost::system::error_code>

View File

@@ -78,7 +78,7 @@ class HttpAsyncClient {
public: public:
HttpAsyncClient(boost::asio::io_context& ioContext); HttpAsyncClient(boost::asio::io_context& ioContext);
std::optional<boost::system::error_code> std::expected<void, boost::system::error_code>
connect( connect(
std::string_view host, std::string_view host,
std::string_view port, std::string_view port,
@@ -86,7 +86,7 @@ public:
std::chrono::steady_clock::duration timeout std::chrono::steady_clock::duration timeout
); );
std::optional<boost::system::error_code> std::expected<void, boost::system::error_code>
send( send(
boost::beast::http::request<boost::beast::http::string_body> request, boost::beast::http::request<boost::beast::http::string_body> request,
boost::asio::yield_context yield, boost::asio::yield_context yield,
@@ -98,6 +98,7 @@ public:
void void
gracefulShutdown(); gracefulShutdown();
void void
disconnect(); 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( WebSocketAsyncClient::connect(
std::string const& host, std::string const& host,
std::string const& port, std::string const& port,
@@ -153,7 +153,7 @@ WebSocketAsyncClient::connect(
boost::beast::get_lowest_layer(stream_).expires_after(timeout); boost::beast::get_lowest_layer(stream_).expires_after(timeout);
stream_.next_layer().async_connect(results, yield[error]); stream_.next_layer().async_connect(results, yield[error]);
if (error) if (error)
return error; return std::unexpected{error};
boost::beast::websocket::stream_base::timeout wsTimeout = boost::beast::websocket::stream_base::timeout wsTimeout =
boost::beast::websocket::stream_base::timeout::suggested(boost::beast::role_type::client); 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]); stream_.async_handshake(fmt::format("{}:{}", host, port), "/", yield[error]);
if (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( WebSocketAsyncClient::send(
boost::asio::yield_context yield, boost::asio::yield_context yield,
std::string_view message, std::string_view message,
@@ -190,8 +190,8 @@ WebSocketAsyncClient::send(
); );
if (error) if (error)
return error; return std::unexpected{error};
return std::nullopt; return {};
} }
std::expected<std::string, boost::system::error_code> std::expected<std::string, boost::system::error_code>

View File

@@ -56,7 +56,7 @@ class WebSocketAsyncClient {
public: public:
WebSocketAsyncClient(boost::asio::io_context& ioContext); WebSocketAsyncClient(boost::asio::io_context& ioContext);
std::optional<boost::system::error_code> std::expected<void, boost::system::error_code>
connect( connect(
std::string const& host, std::string const& host,
std::string const& port, std::string const& port,
@@ -65,7 +65,7 @@ public:
std::vector<WebHeader> additionalHeaders = {} 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); send(boost::asio::yield_context yield, std::string_view message, std::chrono::steady_clock::duration timeout);
std::expected<std::string, boost::system::error_code> 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)); 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(SendReturnType, send, (web::ng::Response, boost::asio::yield_context), (override));
using ReceiveReturnType = std::expected<web::ng::Request, web::ng::Error>; 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)); 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(SendReturnType, send, (web::ng::Response, boost::asio::yield_context), (override));
MOCK_METHOD( MOCK_METHOD(

View File

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

View File

@@ -261,9 +261,9 @@ TEST_F(ServerHttpTest, ClientDisconnects)
{ {
HttpAsyncClient client{ctx_}; HttpAsyncClient client{ctx_};
util::spawn(ctx_, [&](boost::asio::yield_context yield) { 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}); 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(); client.disconnect();
server_->stop(yield); server_->stop(yield);
@@ -312,16 +312,17 @@ TEST_F(ServerHttpTest, OnConnectCheck)
return {}; return {};
}); });
auto maybeError = auto expectedSuccess =
client.connect("127.0.0.1", std::to_string(serverPort), yield, std::chrono::milliseconds{100}); 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 // 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_}, http::request<http::string_body>{http::verb::get, "/", 11, requestMessage_},
yield, yield,
std::chrono::milliseconds{100} std::chrono::milliseconds{100}
); );
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
// Wait for the onConnectCheck to be called // Wait for the onConnectCheck to be called
timer.expires_after(std::chrono::milliseconds{100}); timer.expires_after(std::chrono::milliseconds{100});
@@ -374,16 +375,17 @@ TEST_F(ServerHttpTest, OnConnectCheckFailed)
}); });
util::spawn(ctx_, [&](boost::asio::yield_context yield) { 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}); 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 // 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_}, http::request<http::string_body>{http::verb::get, "/", 11, requestMessage_},
yield, yield,
std::chrono::milliseconds{100} std::chrono::milliseconds{100}
); );
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
auto const response = client.receive(yield, std::chrono::milliseconds{100}); auto const response = client.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(response.has_value()) << response.error().message(); }(); [&]() { 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(); }); 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}); 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_}, http::request<http::string_body>{http::verb::get, "/", 11, requestMessage_},
yield, yield,
std::chrono::milliseconds{100} std::chrono::milliseconds{100}
); );
[&]() { ASSERT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message(); }();
client.gracefulShutdown(); client.gracefulShutdown();
@@ -463,17 +467,17 @@ TEST_F(ServerHttpTest, ClientIsDisconnectedIfServerStopped)
{ {
HttpAsyncClient client{ctx_}; HttpAsyncClient client{ctx_};
util::spawn(ctx_, [&](boost::asio::yield_context yield) { 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}); 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 // 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_}, http::request<http::string_body>{http::verb::get, "/", 11, requestMessage_},
yield, yield,
std::chrono::milliseconds{100} 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}); auto message = client.receive(yield, std::chrono::milliseconds{100});
EXPECT_TRUE(message.has_value()) << message.error().message(); 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}}; Response const response{http::status::ok, "some response", Request{request}};
util::spawn(ctx_, [&](boost::asio::yield_context yield) { 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}); 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}) { for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
maybeError = client.send(request, yield, std::chrono::milliseconds{100}); expectedSuccess = client.send(request, yield, std::chrono::milliseconds{100});
EXPECT_FALSE(maybeError.has_value()) << maybeError->message(); EXPECT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message();
auto const expectedResponse = client.receive(yield, std::chrono::milliseconds{100}); auto const expectedResponse = client.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }(); [&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
@@ -548,9 +552,9 @@ TEST_F(ServerTest, WsClientDisconnects)
WebSocketAsyncClient client{ctx_}; WebSocketAsyncClient client{ctx_};
util::spawn(ctx_, [&](boost::asio::yield_context yield) { 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}); 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(); client.close();
server_->stop(yield); server_->stop(yield);
@@ -570,13 +574,13 @@ TEST_F(ServerTest, WsRequestResponse)
Response const response{http::status::ok, "some response", Request{requestMessage_, headers}}; Response const response{http::status::ok, "some response", Request{requestMessage_, headers}};
util::spawn(ctx_, [&](boost::asio::yield_context yield) { 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}); 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}) { for ([[maybe_unused]] auto i : std::ranges::iota_view{0, 3}) {
maybeError = client.send(yield, requestMessage_, std::chrono::milliseconds{100}); expectedSuccess = client.send(yield, requestMessage_, std::chrono::milliseconds{100});
EXPECT_FALSE(maybeError.has_value()) << maybeError->message(); EXPECT_TRUE(expectedSuccess.has_value()) << expectedSuccess.error().message();
auto const expectedResponse = client.receive(yield, std::chrono::milliseconds{100}); auto const expectedResponse = client.receive(yield, std::chrono::milliseconds{100});
[&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }(); [&]() { ASSERT_TRUE(expectedResponse.has_value()) << expectedResponse.error().message(); }();
@@ -608,10 +612,10 @@ TEST_F(ServerTest, WsClientIsDisconnectedIfServerStopped)
{ {
WebSocketAsyncClient client{ctx_}; WebSocketAsyncClient client{ctx_};
util::spawn(ctx_, [&](boost::asio::yield_context yield) { 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}); client.connect("127.0.0.1", std::to_string(serverPort_), yield, std::chrono::milliseconds{100});
EXPECT_TRUE(maybeError.has_value()); EXPECT_FALSE(expectedSuccess.has_value());
EXPECT_EQ(maybeError.value().value(), static_cast<int>(boost::beast::websocket::error::upgrade_declined)); EXPECT_EQ(expectedSuccess.error().value(), static_cast<int>(boost::beast::websocket::error::upgrade_declined));
ctx_.stop(); ctx_.stop();
}); });

View File

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

View File

@@ -173,9 +173,10 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_NoHandler_Send)
.WillOnce(Return(makeRequest("some_request", headers))) .WillOnce(Return(makeRequest("some_request", headers)))
.WillOnce(Return(makeError(websocket::error::closed))); .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"); 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) { 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(makeRequest(http::request<http::string_body>{http::verb::get, target, 11, requestMessage})))
.WillOnce(Return(makeError(http::error::end_of_stream))); .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"); EXPECT_EQ(response.message(), "Bad target");
auto const httpResponse = std::move(response).intoHttpResponse(); auto const httpResponse = std::move(response).intoHttpResponse();
EXPECT_EQ(httpResponse.result(), http::status::bad_request); EXPECT_EQ(httpResponse.result(), http::status::bad_request);
EXPECT_EQ(httpResponse.version(), 11); EXPECT_EQ(httpResponse.version(), 11);
return std::nullopt; return {};
}); });
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockHttpConnection.get()](Connection const& c) { 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(makeRequest(http::request<http::string_body>{http::verb::acl, "/", 11})))
.WillOnce(Return(makeError(http::error::end_of_stream))); .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"); EXPECT_EQ(response.message(), "Unsupported http method");
return std::nullopt; return {};
}); });
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockHttpConnection.get()](Connection const& c) { 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); 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); EXPECT_EQ(response.message(), responseMessage);
return std::nullopt; return {};
}); });
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) { 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); 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) 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); EXPECT_EQ(*sendingMessage, subscriptionMessage);
return std::nullopt; return {};
}); }
);
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) { EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) {
EXPECT_EQ(&c, connectionPtr); EXPECT_EQ(&c, connectionPtr);
@@ -328,7 +336,7 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, SubscriptionContextIsDisconnec
return Response(http::status::ok, "", request); 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); EXPECT_CALL(onDisconnectHook, Call).After(expectationReceiveCalled);
@@ -367,9 +375,10 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, SubscriptionContextIsNullForHt
return Response(http::status::ok, responseMessage, request); 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); EXPECT_EQ(response.message(), responseMessage);
return std::nullopt; return {};
}); });
EXPECT_CALL( EXPECT_CALL(
@@ -413,9 +422,11 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_Send_Loop)
return Response(http::status::ok, responseMessage, request); 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); EXPECT_EQ(response.message(), responseMessage);
return std::nullopt; return {};
}); });
EXPECT_CALL( EXPECT_CALL(
@@ -456,7 +467,7 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_SendError)
EXPECT_CALL(*mockHttpConnection, send).WillOnce([&responseMessage](Response response, auto&&) { EXPECT_CALL(*mockHttpConnection, send).WillOnce([&responseMessage](Response response, auto&&) {
EXPECT_EQ(response.message(), responseMessage); 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) { 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_CALL(*mockHttpConnectionFromProxy, send).WillOnce([&responseMessage](Response response, auto&&) {
EXPECT_EQ(response.message(), responseMessage); EXPECT_EQ(response.message(), responseMessage);
return makeError(http::error::end_of_stream).error(); return makeError(http::error::end_of_stream);
}); });
EXPECT_CALL(onDisconnectMock, Call) EXPECT_CALL(onDisconnectMock, Call)
@@ -542,12 +553,12 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Stop)
send(testing::ResultOf([](Response const& r) { return r.message(); }, responseMessage), testing::_) send(testing::ResultOf([](Response const& r) { return r.message(); }, responseMessage), testing::_)
) )
.Times(3) .Times(3)
.WillRepeatedly([&](auto&&, auto&&) { .WillRepeatedly([&](auto&&, auto&&) -> std::expected<void, web::ng::Error> {
++numCalls; ++numCalls;
if (numCalls == 3) if (numCalls == 3)
util::spawn(ctx_, [this](auto yield) { connectionHandler.stop(yield); }); util::spawn(ctx_, [this](auto yield) { connectionHandler.stop(yield); });
return std::nullopt; return {};
}); });
EXPECT_CALL( EXPECT_CALL(
@@ -659,9 +670,10 @@ TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send)
return Response(http::status::ok, responseMessage, request); 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); EXPECT_EQ(response.message(), responseMessage);
return std::nullopt; return {};
}); });
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) { 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); 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); EXPECT_EQ(response.message(), responseMessage);
return std::nullopt; return {};
}); });
EXPECT_CALL(onDisconnectMock, Call) EXPECT_CALL(onDisconnectMock, Call)
@@ -738,9 +751,11 @@ TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send_Loop)
return Response(http::status::ok, responseMessage, request); 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); EXPECT_EQ(response.message(), responseMessage);
return std::nullopt; return {};
}); });
EXPECT_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) { 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::_) send(testing::ResultOf([](Response response) { return response.message(); }, responseMessage), testing::_)
) )
.Times(3) .Times(3)
.WillRepeatedly(Return(std::nullopt)); .WillRepeatedly(Return(std::expected<void, web::ng::Error>{}));
EXPECT_CALL( EXPECT_CALL(
*mockWsConnection, *mockWsConnection,
@@ -799,7 +814,7 @@ TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send_Loop_TooMany
) )
) )
.Times(2) .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_CALL(onDisconnectMock, Call).WillOnce([connectionPtr = mockWsConnection.get()](Connection const& c) {
EXPECT_EQ(&c, connectionPtr); EXPECT_EQ(&c, connectionPtr);

View File

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

View File

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