yams/tests/stress/mcp_socket_stress_catch2_test.cpp

67 lines
2.2 KiB
C++

#include <catch2/catch_test_macros.hpp>
// Validates multiplexing over a single socket does not hang
#include <spdlog/spdlog.h>
#include <atomic>
#include <chrono>
#include <future>
#include <vector>
// Ensure Boost.Asio awaitable/co_spawn are visible before including client header
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/use_future.hpp>
#include "integration/daemon/test_daemon_harness.h"
#include <yams/daemon/client/daemon_client.h>
#include <yams/daemon/client/global_io_context.h>
using namespace yams;
using namespace yams::daemon;
TEST_CASE("MCPSocketStressTest.ManyConcurrentRequestsSingleSocket",
"[stress][mcpsocketstresstest]") {
yams::test::DaemonHarness harness;
INFO("Failed to start daemon harness");
REQUIRE(harness.start());
ClientConfig cfg;
cfg.socketPath = harness.socketPath();
cfg.singleUseConnections = false;
cfg.maxInflight = 256;
cfg.requestTimeout = std::chrono::milliseconds(10000);
cfg.headerTimeout = std::chrono::milliseconds(5000);
cfg.bodyTimeout = std::chrono::milliseconds(15000);
cfg.autoStart = false;
DaemonClient client(cfg);
// Fire a burst of concurrent lightweight requests over the single socket
const int concurrency = 64;
const int rounds = 3;
for (int r = 0; r < rounds; ++r) {
auto results =
std::make_shared<std::vector<std::optional<Result<StatusResponse>>>>(concurrency);
std::vector<std::future<void>> futs;
futs.reserve(concurrency);
for (int i = 0; i < concurrency; ++i) {
futs.emplace_back(boost::asio::co_spawn(
GlobalIOContext::instance().get_io_context(),
[&client, results, i]() -> boost::asio::awaitable<void> {
auto res = co_await client.status();
(*results)[i] = std::move(res);
co_return;
},
boost::asio::use_future));
}
for (auto& f : futs)
f.get();
int ok = 0;
for (auto& v : *results)
if (v && v->has_value())
++ok;
spdlog::info("MCPSocketStress round {}: {}/{} OK", r, ok, concurrency);
REQUIRE(ok == concurrency);
}
}