#include "util/AsioContextTestFixture.hpp" #include "util/MockAssert.hpp" #include "util/Taggable.hpp" #include "util/config/ConfigDefinition.hpp" #include "util/config/ConfigValue.hpp" #include "util/config/Types.hpp" #include "web/SubscriptionContextInterface.hpp" #include "web/ng/Connection.hpp" #include "web/ng/Error.hpp" #include "web/ng/SubscriptionContext.hpp" #include "web/ng/impl/MockWsConnection.hpp" #include #include #include #include #include #include #include #include #include #include using namespace web::ng; using namespace util::config; struct NgSubscriptionContextTests : SyncAsioContextTest { SubscriptionContext makeSubscriptionContext( boost::asio::yield_context yield, std::optional maxSendQueueSize = std::nullopt ) { return SubscriptionContext{ tagFactory_, connection_, maxSendQueueSize, yield, errorHandler_.AsStdFunction() }; } protected: util::TagDecoratorFactory tagFactory_{ClioConfigDefinition{ {"log.tag_style", ConfigValue{ConfigType::String}.defaultValue("uint")}, }}; MockWsConnectionImpl connection_{"some ip", boost::beast::flat_buffer{}, tagFactory_}; testing::StrictMock> errorHandler_; }; TEST_F(NgSubscriptionContextTests, Send) { runSpawn([this](boost::asio::yield_context yield) { auto subscriptionContext = makeSubscriptionContext(yield); auto const message = std::make_shared("some message"); EXPECT_CALL(connection_, sendShared) .WillOnce( [&message]( std::shared_ptr sendingMessage, auto&& ) -> std::expected { EXPECT_EQ(sendingMessage, message); return {}; } ); subscriptionContext.send(message); subscriptionContext.disconnect(yield); }); } TEST_F(NgSubscriptionContextTests, SendOrder) { runSpawn([this](boost::asio::yield_context yield) { auto subscriptionContext = makeSubscriptionContext(yield); auto const message1 = std::make_shared("message1"); auto const message2 = std::make_shared("message2"); testing::Sequence const sequence; EXPECT_CALL(connection_, sendShared) .InSequence(sequence) .WillOnce( [&message1]( std::shared_ptr sendingMessage, auto&& ) -> std::expected { EXPECT_EQ(sendingMessage, message1); return {}; } ); EXPECT_CALL(connection_, sendShared) .InSequence(sequence) .WillOnce( [&message2]( std::shared_ptr sendingMessage, auto&& ) -> std::expected { EXPECT_EQ(sendingMessage, message2); return {}; } ); subscriptionContext.send(message1); subscriptionContext.send(message2); subscriptionContext.disconnect(yield); }); } TEST_F(NgSubscriptionContextTests, SendFailed) { runSpawn([this](boost::asio::yield_context yield) { auto subscriptionContext = makeSubscriptionContext(yield); auto const message = std::make_shared("some message"); EXPECT_CALL(connection_, sendShared) .WillOnce([&message](std::shared_ptr sendingMessage, auto&&) { EXPECT_EQ(sendingMessage, message); 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); subscriptionContext.send(message); subscriptionContext.disconnect(yield); }); } TEST_F(NgSubscriptionContextTests, SendTooManySubscriptions) { runSpawn([this](boost::asio::yield_context yield) { auto subscriptionContext = makeSubscriptionContext(yield, 1); auto const message = std::make_shared("message1"); EXPECT_CALL(connection_, sendShared) .WillOnce( [&message]( std::shared_ptr sendingMessage, boost::asio::yield_context innerYield ) -> std::expected { boost::asio::post( innerYield ); // simulate send is slow by switching to another coroutine EXPECT_EQ(sendingMessage, message); return {}; } ); EXPECT_CALL(connection_, close); subscriptionContext.send(message); subscriptionContext.send(message); subscriptionContext.send(message); subscriptionContext.disconnect(yield); }); } TEST_F(NgSubscriptionContextTests, SendAfterDisconnect) { runSpawn([this](boost::asio::yield_context yield) { auto subscriptionContext = makeSubscriptionContext(yield); auto const message = std::make_shared("some message"); subscriptionContext.disconnect(yield); subscriptionContext.send(message); }); } TEST_F(NgSubscriptionContextTests, OnDisconnect) { testing::StrictMock> onDisconnect; runSpawn([&](boost::asio::yield_context yield) { auto subscriptionContext = makeSubscriptionContext(yield); subscriptionContext.onDisconnect(onDisconnect.AsStdFunction()); EXPECT_CALL(onDisconnect, Call(&subscriptionContext)); subscriptionContext.disconnect(yield); }); } TEST_F(NgSubscriptionContextTests, SetApiSubversion) { runSpawn([this](boost::asio::yield_context yield) { auto subscriptionContext = makeSubscriptionContext(yield); subscriptionContext.setApiSubversion(42); EXPECT_EQ(subscriptionContext.apiSubversion(), 42); subscriptionContext.disconnect(yield); }); } struct NgSubscriptionContextAssertTests : common::util::WithMockAssertNoThrow, NgSubscriptionContextTests {}; TEST_F(NgSubscriptionContextAssertTests, AssertFailsWhenNotDisconnected) { runSpawn([&](boost::asio::yield_context yield) { EXPECT_CLIO_ASSERT_FAIL({ auto subscriptionContext = makeSubscriptionContext(yield); }); }); }