mirror of
				https://github.com/XRPLF/clio.git
				synced 2025-11-04 11:55:51 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c795cf371a | ||
| 
						 | 
					e135aa49d5 | 
@@ -163,10 +163,12 @@ public:
 | 
			
		||||
        static auto const rpcSpec = RpcSpec{
 | 
			
		||||
            {JS(ledger_hash), validation::Uint256HexStringValidator},
 | 
			
		||||
            {JS(ledger_index), validation::LedgerIndexValidator},
 | 
			
		||||
            // validate quoteAsset in accordance to the currency code found in XRPL doc:
 | 
			
		||||
            // validate quoteAsset and base_asset in accordance to the currency code found in XRPL doc:
 | 
			
		||||
            // https://xrpl.org/docs/references/protocol/data-types/currency-formats#currency-codes
 | 
			
		||||
            // usually Clio returns rpcMALFORMED_CURRENCY , return InvalidParam here just to mimic rippled
 | 
			
		||||
            {JS(base_asset), validation::Required{}, validation::Type<std::string>{}},
 | 
			
		||||
            {JS(base_asset),
 | 
			
		||||
             validation::Required{},
 | 
			
		||||
             meta::WithCustomError{validation::CurrencyValidator, Status(RippledError::rpcINVALID_PARAMS)}},
 | 
			
		||||
            {JS(quote_asset),
 | 
			
		||||
             validation::Required{},
 | 
			
		||||
             meta::WithCustomError{validation::CurrencyValidator, Status(RippledError::rpcINVALID_PARAMS)}},
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ add_library(clio_testing_common)
 | 
			
		||||
 | 
			
		||||
target_sources(
 | 
			
		||||
  clio_testing_common PRIVATE util/StringUtils.cpp util/TestHttpServer.cpp util/TestWsServer.cpp util/TestObject.cpp
 | 
			
		||||
                              util/AssignRandomPort.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
include(deps/gtest)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								tests/common/util/AssignRandomPort.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tests/common/util/AssignRandomPort.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, the clio developers.
 | 
			
		||||
 | 
			
		||||
    Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
    purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
    copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
    THE  SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
    WITH  REGARD  TO  THIS  SOFTWARE  INCLUDING  ALL  IMPLIED  WARRANTIES  OF
 | 
			
		||||
    MERCHANTABILITY  AND  FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
    ANY  SPECIAL,  DIRECT,  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
    WHATSOEVER  RESULTING  FROM  LOSS  OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
    ACTION  OF  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include "util/AssignRandomPort.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/asio/io_context.hpp>
 | 
			
		||||
#include <boost/asio/ip/tcp.hpp>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
using tcp = boost::asio::ip::tcp;
 | 
			
		||||
 | 
			
		||||
namespace tests::util {
 | 
			
		||||
 | 
			
		||||
uint32_t
 | 
			
		||||
generateFreePort()
 | 
			
		||||
{
 | 
			
		||||
    boost::asio::io_context io_context;
 | 
			
		||||
    tcp::acceptor acceptor(io_context);
 | 
			
		||||
    tcp::endpoint const endpoint(tcp::v4(), 0);
 | 
			
		||||
 | 
			
		||||
    acceptor.open(endpoint.protocol());
 | 
			
		||||
    acceptor.set_option(tcp::acceptor::reuse_address(true));
 | 
			
		||||
    acceptor.bind(endpoint);
 | 
			
		||||
 | 
			
		||||
    return acceptor.local_endpoint().port();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace tests::util
 | 
			
		||||
							
								
								
									
										29
									
								
								tests/common/util/AssignRandomPort.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/common/util/AssignRandomPort.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of clio: https://github.com/XRPLF/clio
 | 
			
		||||
    Copyright (c) 2024, the clio developers.
 | 
			
		||||
 | 
			
		||||
    Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
    purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
    copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
    THE  SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
    WITH  REGARD  TO  THIS  SOFTWARE  INCLUDING  ALL  IMPLIED  WARRANTIES  OF
 | 
			
		||||
    MERCHANTABILITY  AND  FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
    ANY  SPECIAL,  DIRECT,  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
    WHATSOEVER  RESULTING  FROM  LOSS  OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
    ACTION  OF  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
namespace tests::util {
 | 
			
		||||
 | 
			
		||||
uint32_t
 | 
			
		||||
generateFreePort();
 | 
			
		||||
 | 
			
		||||
}  // namespace tests::util
 | 
			
		||||
@@ -82,7 +82,7 @@ struct WithMockXrpLedgerAPIService : virtual ::testing::Test {
 | 
			
		||||
    WithMockXrpLedgerAPIService(std::string serverAddress)
 | 
			
		||||
    {
 | 
			
		||||
        grpc::ServerBuilder builder;
 | 
			
		||||
        builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials());
 | 
			
		||||
        builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials(), &port_);
 | 
			
		||||
        builder.RegisterService(&mockXrpLedgerAPIService);
 | 
			
		||||
        server_ = builder.BuildAndStart();
 | 
			
		||||
        serverThread_ = std::thread([this] { server_->Wait(); });
 | 
			
		||||
@@ -94,11 +94,17 @@ struct WithMockXrpLedgerAPIService : virtual ::testing::Test {
 | 
			
		||||
        serverThread_.join();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int
 | 
			
		||||
    getXRPLMockPort() const
 | 
			
		||||
    {
 | 
			
		||||
        return port_;
 | 
			
		||||
    }
 | 
			
		||||
    MockXrpLedgerAPIService mockXrpLedgerAPIService;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<grpc::Server> server_;
 | 
			
		||||
    std::thread serverThread_;
 | 
			
		||||
    int port_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace tests::util
 | 
			
		||||
 
 | 
			
		||||
@@ -105,9 +105,9 @@ doSession(
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
TestHttpServer::TestHttpServer(boost::asio::io_context& context, std::string host, int const port) : acceptor_(context)
 | 
			
		||||
TestHttpServer::TestHttpServer(boost::asio::io_context& context, std::string host) : acceptor_(context)
 | 
			
		||||
{
 | 
			
		||||
    boost::asio::ip::tcp::endpoint const endpoint(boost::asio::ip::make_address(host), port);
 | 
			
		||||
    boost::asio::ip::tcp::endpoint const endpoint(boost::asio::ip::make_address(host), 0);
 | 
			
		||||
    acceptor_.open(endpoint.protocol());
 | 
			
		||||
    acceptor_.set_option(asio::socket_base::reuse_address(true));
 | 
			
		||||
    acceptor_.bind(endpoint);
 | 
			
		||||
@@ -134,3 +134,9 @@ TestHttpServer::handleRequest(TestHttpServer::RequestHandler handler, bool const
 | 
			
		||||
        boost::asio::detached
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string
 | 
			
		||||
TestHttpServer::port() const
 | 
			
		||||
{
 | 
			
		||||
    return std::to_string(acceptor_.local_endpoint().port());
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -41,9 +41,8 @@ public:
 | 
			
		||||
     *
 | 
			
		||||
     * @param context boost::asio::io_context to use for networking
 | 
			
		||||
     * @param host host to bind to
 | 
			
		||||
     * @param port port to bind to
 | 
			
		||||
     */
 | 
			
		||||
    TestHttpServer(boost::asio::io_context& context, std::string host, int port);
 | 
			
		||||
    TestHttpServer(boost::asio::io_context& context, std::string host);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Start the server
 | 
			
		||||
@@ -56,6 +55,14 @@ public:
 | 
			
		||||
    void
 | 
			
		||||
    handleRequest(RequestHandler handler, bool allowToFail = false);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Return the port HTTP server is connected to
 | 
			
		||||
     *
 | 
			
		||||
     * @return string port number
 | 
			
		||||
     */
 | 
			
		||||
    std::string
 | 
			
		||||
    port() const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    boost::asio::ip::tcp::acceptor acceptor_;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -105,14 +105,20 @@ TestWsConnection::headers() const
 | 
			
		||||
    return headers_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TestWsServer::TestWsServer(asio::io_context& context, std::string const& host, int port) : acceptor_(context)
 | 
			
		||||
TestWsServer::TestWsServer(asio::io_context& context, std::string const& host) : acceptor_(context)
 | 
			
		||||
{
 | 
			
		||||
    auto endpoint = asio::ip::tcp::endpoint(boost::asio::ip::make_address(host), port);
 | 
			
		||||
    auto endpoint = asio::ip::tcp::endpoint(boost::asio::ip::make_address(host), 0);
 | 
			
		||||
    acceptor_.open(endpoint.protocol());
 | 
			
		||||
    acceptor_.set_option(asio::socket_base::reuse_address(true));
 | 
			
		||||
    acceptor_.bind(endpoint);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string
 | 
			
		||||
TestWsServer::port() const
 | 
			
		||||
{
 | 
			
		||||
    return std::to_string(this->acceptor_.local_endpoint().port());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::expected<TestWsConnection, util::requests::RequestError>
 | 
			
		||||
TestWsServer::acceptConnection(asio::yield_context yield)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,10 @@ class TestWsServer {
 | 
			
		||||
    boost::asio::ip::tcp::acceptor acceptor_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    TestWsServer(boost::asio::io_context& context, std::string const& host, int port);
 | 
			
		||||
    TestWsServer(boost::asio::io_context& context, std::string const& host);
 | 
			
		||||
 | 
			
		||||
    std::string
 | 
			
		||||
    port() const;
 | 
			
		||||
 | 
			
		||||
    std::expected<TestWsConnection, util::requests::RequestError>
 | 
			
		||||
    acceptConnection(boost::asio::yield_context yield);
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,13 @@
 | 
			
		||||
using namespace etl::impl;
 | 
			
		||||
 | 
			
		||||
struct ForwardingSourceTests : SyncAsioContextTest {
 | 
			
		||||
    TestWsServer server_{ctx, "0.0.0.0", 11114};
 | 
			
		||||
    ForwardingSource forwardingSource{"127.0.0.1", "11114", std::chrono::milliseconds{1}, std::chrono::milliseconds{1}};
 | 
			
		||||
    TestWsServer server_{ctx, "0.0.0.0"};
 | 
			
		||||
    ForwardingSource forwardingSource{
 | 
			
		||||
        "127.0.0.1",
 | 
			
		||||
        server_.port(),
 | 
			
		||||
        std::chrono::milliseconds{1},
 | 
			
		||||
        std::chrono::milliseconds{1}
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TEST_F(ForwardingSourceTests, ConnectionFailed)
 | 
			
		||||
 
 | 
			
		||||
@@ -42,9 +42,9 @@ using namespace etl::impl;
 | 
			
		||||
 | 
			
		||||
struct GrpcSourceTests : NoLoggerFixture, util::prometheus::WithPrometheus, tests::util::WithMockXrpLedgerAPIService {
 | 
			
		||||
    GrpcSourceTests()
 | 
			
		||||
        : WithMockXrpLedgerAPIService("localhost:55051")
 | 
			
		||||
        : WithMockXrpLedgerAPIService("localhost:0")
 | 
			
		||||
        , mockBackend_(std::make_shared<testing::StrictMock<MockBackend>>(util::Config{}))
 | 
			
		||||
        , grpcSource_("127.0.0.1", "55051", mockBackend_)
 | 
			
		||||
        , grpcSource_("127.0.0.1", std::to_string(getXRPLMockPort()), mockBackend_)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include "etl/impl/SubscriptionSource.hpp"
 | 
			
		||||
#include "util/AssignRandomPort.hpp"
 | 
			
		||||
#include "util/Fixtures.hpp"
 | 
			
		||||
#include "util/MockNetworkValidatedLedgers.hpp"
 | 
			
		||||
#include "util/MockSubscriptionManager.hpp"
 | 
			
		||||
@@ -31,6 +32,7 @@
 | 
			
		||||
#include <gtest/gtest.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
@@ -46,8 +48,7 @@ struct SubscriptionSourceConnectionTests : public NoLoggerFixture {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boost::asio::io_context ioContext_;
 | 
			
		||||
 | 
			
		||||
    TestWsServer wsServer_{ioContext_, "0.0.0.0", 11113};
 | 
			
		||||
    TestWsServer wsServer_{ioContext_, "0.0.0.0"};
 | 
			
		||||
 | 
			
		||||
    StrictMockNetworkValidatedLedgersPtr networkValidatedLedgers_;
 | 
			
		||||
    StrictMockSubscriptionManagerSharedPtr subscriptionManager_;
 | 
			
		||||
@@ -59,7 +60,7 @@ struct SubscriptionSourceConnectionTests : public NoLoggerFixture {
 | 
			
		||||
    SubscriptionSource subscriptionSource_{
 | 
			
		||||
        ioContext_,
 | 
			
		||||
        "127.0.0.1",
 | 
			
		||||
        "11113",
 | 
			
		||||
        wsServer_.port(),
 | 
			
		||||
        networkValidatedLedgers_,
 | 
			
		||||
        subscriptionManager_,
 | 
			
		||||
        onConnectHook_.AsStdFunction(),
 | 
			
		||||
 
 | 
			
		||||
@@ -144,6 +144,38 @@ generateTestValuesForParametersTest()
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Required field 'base_asset' missing"
 | 
			
		||||
        },
 | 
			
		||||
        GetAggregatePriceParamTestCaseBundle{
 | 
			
		||||
            "invalid_base_asset",
 | 
			
		||||
            R"({
 | 
			
		||||
                    "quote_asset" : "USD",
 | 
			
		||||
                    "base_asset": "asdf",
 | 
			
		||||
                    "oracles": 
 | 
			
		||||
                    [
 | 
			
		||||
                        {
 | 
			
		||||
                            "account": "rGh1VZCRBJY6rJiaFpD4LZtyHiuCkC8aeD",
 | 
			
		||||
                            "oracle_document_id": 2
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                })",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."
 | 
			
		||||
        },
 | 
			
		||||
        GetAggregatePriceParamTestCaseBundle{
 | 
			
		||||
            "invalid_base_asset2",
 | 
			
		||||
            R"({
 | 
			
		||||
                    "quote_asset" : "USD",
 | 
			
		||||
                    "base_asset": "+aa",
 | 
			
		||||
                    "oracles": 
 | 
			
		||||
                    [
 | 
			
		||||
                        {
 | 
			
		||||
                            "account": "rGh1VZCRBJY6rJiaFpD4LZtyHiuCkC8aeD",
 | 
			
		||||
                            "oracle_document_id": 2
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                })",
 | 
			
		||||
            "invalidParams",
 | 
			
		||||
            "Invalid parameters."
 | 
			
		||||
        },
 | 
			
		||||
        GetAggregatePriceParamTestCaseBundle{
 | 
			
		||||
            "no_quote_asset",
 | 
			
		||||
            R"({
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include "util/AssignRandomPort.hpp"
 | 
			
		||||
#include "util/Fixtures.hpp"
 | 
			
		||||
#include "util/TestHttpServer.hpp"
 | 
			
		||||
#include "util/requests/RequestBuilder.hpp"
 | 
			
		||||
@@ -31,6 +32,7 @@
 | 
			
		||||
#include <gtest/gtest.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <expected>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
@@ -50,8 +52,8 @@ struct RequestBuilderTestBundle {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct RequestBuilderTestBase : SyncAsioContextTest {
 | 
			
		||||
    TestHttpServer server{ctx, "0.0.0.0", 11111};
 | 
			
		||||
    RequestBuilder builder{"localhost", "11111"};
 | 
			
		||||
    TestHttpServer server{ctx, "0.0.0.0"};
 | 
			
		||||
    RequestBuilder builder{"localhost", server.port()};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct RequestBuilderTest : RequestBuilderTestBase, testing::WithParamInterface<RequestBuilderTestBundle> {};
 | 
			
		||||
@@ -182,7 +184,7 @@ TEST_F(RequestBuilderTest, ResolveError)
 | 
			
		||||
 | 
			
		||||
TEST_F(RequestBuilderTest, ConnectionError)
 | 
			
		||||
{
 | 
			
		||||
    builder = RequestBuilder{"localhost", "11112"};
 | 
			
		||||
    builder = RequestBuilder{"localhost", std::to_string(tests::util::generateFreePort())};
 | 
			
		||||
    builder.setTimeout(std::chrono::milliseconds{1});
 | 
			
		||||
    runSpawn([this](asio::yield_context yield) {
 | 
			
		||||
        auto const response = builder.getPlain(yield);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include "util/AssignRandomPort.hpp"
 | 
			
		||||
#include "util/Fixtures.hpp"
 | 
			
		||||
#include "util/TestWsServer.hpp"
 | 
			
		||||
#include "util/requests/Types.hpp"
 | 
			
		||||
@@ -29,6 +30,7 @@
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <expected>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
@@ -41,8 +43,8 @@ namespace asio = boost::asio;
 | 
			
		||||
namespace http = boost::beast::http;
 | 
			
		||||
 | 
			
		||||
struct WsConnectionTestsBase : SyncAsioContextTest {
 | 
			
		||||
    WsConnectionBuilder builder{"localhost", "11112"};
 | 
			
		||||
    TestWsServer server{ctx, "0.0.0.0", 11112};
 | 
			
		||||
    TestWsServer server{ctx, "0.0.0.0"};
 | 
			
		||||
    WsConnectionBuilder builder{"localhost", server.port()};
 | 
			
		||||
 | 
			
		||||
    template <typename T, typename E>
 | 
			
		||||
    T
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include "util/AssignRandomPort.hpp"
 | 
			
		||||
#include "util/Fixtures.hpp"
 | 
			
		||||
#include "util/MockPrometheus.hpp"
 | 
			
		||||
#include "util/TestHttpSyncClient.hpp"
 | 
			
		||||
@@ -43,13 +44,17 @@
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
#include <gtest/gtest.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
@@ -57,37 +62,48 @@
 | 
			
		||||
using namespace util;
 | 
			
		||||
using namespace web::impl;
 | 
			
		||||
using namespace web;
 | 
			
		||||
using namespace boost::json;
 | 
			
		||||
 | 
			
		||||
constexpr static auto JSONData = R"JSON(
 | 
			
		||||
    {
 | 
			
		||||
        "server":{
 | 
			
		||||
            "ip":"0.0.0.0",
 | 
			
		||||
            "port":8888
 | 
			
		||||
        },
 | 
			
		||||
        "dos_guard": {
 | 
			
		||||
            "max_fetches": 100,
 | 
			
		||||
            "sweep_interval": 1000,
 | 
			
		||||
            "max_connections": 2,
 | 
			
		||||
            "max_requests": 3,
 | 
			
		||||
            "whitelist": ["127.0.0.1"]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
)JSON";
 | 
			
		||||
std::string
 | 
			
		||||
generateJSONWithDynamicPort(std::string_view port)
 | 
			
		||||
{
 | 
			
		||||
    return fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
            "server": {{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": {}
 | 
			
		||||
            }},
 | 
			
		||||
            "dos_guard": {{
 | 
			
		||||
                "max_fetches": 100,
 | 
			
		||||
                "sweep_interval": 1000,
 | 
			
		||||
                "max_connections": 2,
 | 
			
		||||
                "max_requests": 3,
 | 
			
		||||
                "whitelist": ["127.0.0.1"]
 | 
			
		||||
            }}
 | 
			
		||||
        }})JSON",
 | 
			
		||||
        port
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr static auto JSONDataOverload = R"JSON(
 | 
			
		||||
    {
 | 
			
		||||
        "server":{
 | 
			
		||||
            "ip":"0.0.0.0",
 | 
			
		||||
            "port":8888
 | 
			
		||||
        },
 | 
			
		||||
        "dos_guard": {
 | 
			
		||||
            "max_fetches": 100,
 | 
			
		||||
            "sweep_interval": 1000,
 | 
			
		||||
            "max_connections": 2,
 | 
			
		||||
            "max_requests": 1
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
)JSON";
 | 
			
		||||
std::string
 | 
			
		||||
generateJSONDataOverload(std::string_view port)
 | 
			
		||||
{
 | 
			
		||||
    return fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
            "server": {{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": {}
 | 
			
		||||
            }},
 | 
			
		||||
            "dos_guard": {{
 | 
			
		||||
                "max_fetches": 100,
 | 
			
		||||
                "sweep_interval": 1000,
 | 
			
		||||
                "max_connections": 2,
 | 
			
		||||
                "max_requests": 1
 | 
			
		||||
            }}
 | 
			
		||||
        }})JSON",
 | 
			
		||||
        port
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// for testing, we use a self-signed certificate
 | 
			
		||||
std::optional<ssl::context>
 | 
			
		||||
@@ -168,12 +184,13 @@ protected:
 | 
			
		||||
 | 
			
		||||
    // this ctx is for dos timer
 | 
			
		||||
    boost::asio::io_context ctxSync;
 | 
			
		||||
    Config cfg{boost::json::parse(JSONData)};
 | 
			
		||||
    std::string const port = std::to_string(tests::util::generateFreePort());
 | 
			
		||||
    Config cfg{parse(generateJSONWithDynamicPort(port))};
 | 
			
		||||
    IntervalSweepHandler sweepHandler = web::IntervalSweepHandler{cfg, ctxSync};
 | 
			
		||||
    WhitelistHandler whitelistHandler = web::WhitelistHandler{cfg};
 | 
			
		||||
    DOSGuard dosGuard = web::DOSGuard{cfg, whitelistHandler, sweepHandler};
 | 
			
		||||
 | 
			
		||||
    Config cfgOverload{boost::json::parse(JSONDataOverload)};
 | 
			
		||||
    Config cfgOverload{parse(generateJSONDataOverload(port))};
 | 
			
		||||
    IntervalSweepHandler sweepHandlerOverload = web::IntervalSweepHandler{cfgOverload, ctxSync};
 | 
			
		||||
    WhitelistHandler whitelistHandlerOverload = web::WhitelistHandler{cfgOverload};
 | 
			
		||||
    DOSGuard dosGuardOverload = web::DOSGuard{cfgOverload, whitelistHandlerOverload, sweepHandlerOverload};
 | 
			
		||||
@@ -250,7 +267,7 @@ TEST_F(WebServerTest, Http)
 | 
			
		||||
{
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
 | 
			
		||||
    auto const res = HttpSyncClient::syncPost("localhost", "8888", R"({"Hello":1})");
 | 
			
		||||
    auto const res = HttpSyncClient::syncPost("localhost", port, R"({"Hello":1})");
 | 
			
		||||
    EXPECT_EQ(res, R"({"Hello":1})");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -259,7 +276,7 @@ TEST_F(WebServerTest, Ws)
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
 | 
			
		||||
    WebSocketSyncClient wsClient;
 | 
			
		||||
    wsClient.connect("localhost", "8888");
 | 
			
		||||
    wsClient.connect("localhost", port);
 | 
			
		||||
    auto const res = wsClient.syncPost(R"({"Hello":1})");
 | 
			
		||||
    EXPECT_EQ(res, R"({"Hello":1})");
 | 
			
		||||
    wsClient.disconnect();
 | 
			
		||||
@@ -269,7 +286,7 @@ TEST_F(WebServerTest, HttpInternalError)
 | 
			
		||||
{
 | 
			
		||||
    auto e = std::make_shared<ExceptionExecutor>();
 | 
			
		||||
    auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
 | 
			
		||||
    auto const res = HttpSyncClient::syncPost("localhost", "8888", R"({})");
 | 
			
		||||
    auto const res = HttpSyncClient::syncPost("localhost", port, R"({})");
 | 
			
		||||
    EXPECT_EQ(
 | 
			
		||||
        res,
 | 
			
		||||
        R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})"
 | 
			
		||||
@@ -281,7 +298,7 @@ TEST_F(WebServerTest, WsInternalError)
 | 
			
		||||
    auto e = std::make_shared<ExceptionExecutor>();
 | 
			
		||||
    auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
 | 
			
		||||
    WebSocketSyncClient wsClient;
 | 
			
		||||
    wsClient.connect("localhost", "8888");
 | 
			
		||||
    wsClient.connect("localhost", port);
 | 
			
		||||
    auto const res = wsClient.syncPost(R"({"id":"id1"})");
 | 
			
		||||
    wsClient.disconnect();
 | 
			
		||||
    EXPECT_EQ(
 | 
			
		||||
@@ -295,7 +312,7 @@ TEST_F(WebServerTest, WsInternalErrorNotJson)
 | 
			
		||||
    auto e = std::make_shared<ExceptionExecutor>();
 | 
			
		||||
    auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
 | 
			
		||||
    WebSocketSyncClient wsClient;
 | 
			
		||||
    wsClient.connect("localhost", "8888");
 | 
			
		||||
    wsClient.connect("localhost", port);
 | 
			
		||||
    auto const res = wsClient.syncPost("not json");
 | 
			
		||||
    wsClient.disconnect();
 | 
			
		||||
    EXPECT_EQ(
 | 
			
		||||
@@ -310,7 +327,7 @@ TEST_F(WebServerTest, Https)
 | 
			
		||||
    auto sslCtx = parseCertsForTest();
 | 
			
		||||
    auto const ctxSslRef = sslCtx ? std::optional<std::reference_wrapper<ssl::context>>{sslCtx.value()} : std::nullopt;
 | 
			
		||||
    auto const server = makeServerSync(cfg, ctx, ctxSslRef, dosGuard, e);
 | 
			
		||||
    auto const res = HttpsSyncClient::syncPost("localhost", "8888", R"({"Hello":1})");
 | 
			
		||||
    auto const res = HttpsSyncClient::syncPost("localhost", port, R"({"Hello":1})");
 | 
			
		||||
    EXPECT_EQ(res, R"({"Hello":1})");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -322,7 +339,7 @@ TEST_F(WebServerTest, Wss)
 | 
			
		||||
 | 
			
		||||
    auto server = makeServerSync(cfg, ctx, ctxSslRef, dosGuard, e);
 | 
			
		||||
    WebServerSslSyncClient wsClient;
 | 
			
		||||
    wsClient.connect("localhost", "8888");
 | 
			
		||||
    wsClient.connect("localhost", port);
 | 
			
		||||
    auto const res = wsClient.syncPost(R"({"Hello":1})");
 | 
			
		||||
    EXPECT_EQ(res, R"({"Hello":1})");
 | 
			
		||||
    wsClient.disconnect();
 | 
			
		||||
@@ -332,9 +349,9 @@ TEST_F(WebServerTest, HttpRequestOverload)
 | 
			
		||||
{
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
 | 
			
		||||
    auto res = HttpSyncClient::syncPost("localhost", "8888", R"({})");
 | 
			
		||||
    auto res = HttpSyncClient::syncPost("localhost", port, R"({})");
 | 
			
		||||
    EXPECT_EQ(res, "{}");
 | 
			
		||||
    res = HttpSyncClient::syncPost("localhost", "8888", R"({})");
 | 
			
		||||
    res = HttpSyncClient::syncPost("localhost", port, R"({})");
 | 
			
		||||
    EXPECT_EQ(
 | 
			
		||||
        res,
 | 
			
		||||
        R"({"error":"slowDown","error_code":10,"error_message":"You are placing too much load on the server.","status":"error","type":"response"})"
 | 
			
		||||
@@ -346,12 +363,12 @@ TEST_F(WebServerTest, WsRequestOverload)
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
 | 
			
		||||
    WebSocketSyncClient wsClient;
 | 
			
		||||
    wsClient.connect("localhost", "8888");
 | 
			
		||||
    wsClient.connect("localhost", port);
 | 
			
		||||
    auto res = wsClient.syncPost(R"({})");
 | 
			
		||||
    wsClient.disconnect();
 | 
			
		||||
    EXPECT_EQ(res, "{}");
 | 
			
		||||
    WebSocketSyncClient wsClient2;
 | 
			
		||||
    wsClient2.connect("localhost", "8888");
 | 
			
		||||
    wsClient2.connect("localhost", port);
 | 
			
		||||
    res = wsClient2.syncPost(R"({})");
 | 
			
		||||
    wsClient2.disconnect();
 | 
			
		||||
    EXPECT_EQ(
 | 
			
		||||
@@ -365,7 +382,7 @@ TEST_F(WebServerTest, HttpPayloadOverload)
 | 
			
		||||
    std::string const s100(100, 'a');
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
 | 
			
		||||
    auto const res = HttpSyncClient::syncPost("localhost", "8888", fmt::format(R"({{"payload":"{}"}})", s100));
 | 
			
		||||
    auto const res = HttpSyncClient::syncPost("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100));
 | 
			
		||||
    EXPECT_EQ(
 | 
			
		||||
        res,
 | 
			
		||||
        R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})"
 | 
			
		||||
@@ -378,7 +395,7 @@ TEST_F(WebServerTest, WsPayloadOverload)
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
 | 
			
		||||
    WebSocketSyncClient wsClient;
 | 
			
		||||
    wsClient.connect("localhost", "8888");
 | 
			
		||||
    wsClient.connect("localhost", port);
 | 
			
		||||
    auto const res = wsClient.syncPost(fmt::format(R"({{"payload":"{}"}})", s100));
 | 
			
		||||
    wsClient.disconnect();
 | 
			
		||||
    EXPECT_EQ(
 | 
			
		||||
@@ -393,13 +410,13 @@ TEST_F(WebServerTest, WsTooManyConnection)
 | 
			
		||||
    auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
 | 
			
		||||
    // max connection is 2, exception should happen when the third connection is made
 | 
			
		||||
    WebSocketSyncClient wsClient1;
 | 
			
		||||
    wsClient1.connect("localhost", "8888");
 | 
			
		||||
    wsClient1.connect("localhost", port);
 | 
			
		||||
    WebSocketSyncClient wsClient2;
 | 
			
		||||
    wsClient2.connect("localhost", "8888");
 | 
			
		||||
    wsClient2.connect("localhost", port);
 | 
			
		||||
    bool exceptionThrown = false;
 | 
			
		||||
    try {
 | 
			
		||||
        WebSocketSyncClient wsClient3;
 | 
			
		||||
        wsClient3.connect("localhost", "8888");
 | 
			
		||||
        wsClient3.connect("localhost", port);
 | 
			
		||||
    } catch (boost::system::system_error const& ex) {
 | 
			
		||||
        exceptionThrown = true;
 | 
			
		||||
        EXPECT_EQ(ex.code(), boost::beast::websocket::error::upgrade_declined);
 | 
			
		||||
@@ -409,45 +426,65 @@ TEST_F(WebServerTest, WsTooManyConnection)
 | 
			
		||||
    EXPECT_TRUE(exceptionThrown);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static auto constexpr JSONServerConfigWithAdminPassword = R"JSON(
 | 
			
		||||
    {
 | 
			
		||||
        "server":{
 | 
			
		||||
            "ip": "0.0.0.0",
 | 
			
		||||
            "port": 8888,
 | 
			
		||||
            "admin_password": "secret"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
)JSON";
 | 
			
		||||
std::string
 | 
			
		||||
JSONServerConfigWithAdminPassword(uint32_t const port)
 | 
			
		||||
{
 | 
			
		||||
    return fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
            "server": {{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": {},
 | 
			
		||||
                "admin_password": "secret"
 | 
			
		||||
            }}
 | 
			
		||||
        }})JSON",
 | 
			
		||||
        port
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static auto constexpr JSONServerConfigWithLocalAdmin = R"JSON(
 | 
			
		||||
    {
 | 
			
		||||
        "server":{
 | 
			
		||||
            "ip": "0.0.0.0",
 | 
			
		||||
            "port": 8888,
 | 
			
		||||
            "local_admin": true
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
)JSON";
 | 
			
		||||
std::string
 | 
			
		||||
JSONServerConfigWithLocalAdmin(uint32_t const port)
 | 
			
		||||
{
 | 
			
		||||
    return fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
            "server": {{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": {},
 | 
			
		||||
                "local_admin": true
 | 
			
		||||
            }}
 | 
			
		||||
        }})JSON",
 | 
			
		||||
        port
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static auto constexpr JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse = R"JSON(
 | 
			
		||||
    {
 | 
			
		||||
        "server":{
 | 
			
		||||
            "ip": "0.0.0.0",
 | 
			
		||||
            "port": 8888,
 | 
			
		||||
            "admin_password": "secret",
 | 
			
		||||
            "local_admin": false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
)JSON";
 | 
			
		||||
std::string
 | 
			
		||||
JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse(uint32_t const port)
 | 
			
		||||
{
 | 
			
		||||
    return fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
            "server": {{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": {},
 | 
			
		||||
                "admin_password": "secret",
 | 
			
		||||
                "local_admin": false
 | 
			
		||||
            }}
 | 
			
		||||
        }})JSON",
 | 
			
		||||
        port
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static auto constexpr JSONServerConfigWithNoSpecifiedAdmin = R"JSON(
 | 
			
		||||
    {
 | 
			
		||||
        "server":{
 | 
			
		||||
            "ip": "0.0.0.0",
 | 
			
		||||
            "port": 8888
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
)JSON";
 | 
			
		||||
std::string
 | 
			
		||||
JSONServerConfigWithNoSpecifiedAdmin(uint32_t const port)
 | 
			
		||||
{
 | 
			
		||||
    return fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
            "server": {{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": {}
 | 
			
		||||
            }}
 | 
			
		||||
        }})JSON",
 | 
			
		||||
        port
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// get this value from online sha256 generator
 | 
			
		||||
static auto constexpr SecertSha256 = "2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b";
 | 
			
		||||
@@ -478,10 +515,11 @@ class WebServerAdminTest : public WebServerTest, public ::testing::WithParamInte
 | 
			
		||||
TEST_P(WebServerAdminTest, WsAdminCheck)
 | 
			
		||||
{
 | 
			
		||||
    auto e = std::make_shared<AdminCheckExecutor>();
 | 
			
		||||
    Config const serverConfig{boost::json::parse(GetParam().config)};
 | 
			
		||||
    Config const serverConfig{parse(GetParam().config)};
 | 
			
		||||
    auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuardOverload, e);
 | 
			
		||||
    WebSocketSyncClient wsClient;
 | 
			
		||||
    wsClient.connect("localhost", "8888", GetParam().headers);
 | 
			
		||||
    uint32_t webServerPort = serverConfig.value<uint32_t>("server.port");
 | 
			
		||||
    wsClient.connect("localhost", std::to_string(webServerPort), GetParam().headers);
 | 
			
		||||
    std::string const request = "Why hello";
 | 
			
		||||
    auto const res = wsClient.syncPost(request);
 | 
			
		||||
    wsClient.disconnect();
 | 
			
		||||
@@ -491,10 +529,11 @@ TEST_P(WebServerAdminTest, WsAdminCheck)
 | 
			
		||||
TEST_P(WebServerAdminTest, HttpAdminCheck)
 | 
			
		||||
{
 | 
			
		||||
    auto e = std::make_shared<AdminCheckExecutor>();
 | 
			
		||||
    Config const serverConfig{boost::json::parse(GetParam().config)};
 | 
			
		||||
    Config const serverConfig{parse(GetParam().config)};
 | 
			
		||||
    auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuardOverload, e);
 | 
			
		||||
    std::string const request = "Why hello";
 | 
			
		||||
    auto const res = HttpSyncClient::syncPost("localhost", "8888", request, GetParam().headers);
 | 
			
		||||
    uint32_t webServerPort = serverConfig.value<uint32_t>("server.port");
 | 
			
		||||
    auto const res = HttpSyncClient::syncPost("localhost", std::to_string(webServerPort), request, GetParam().headers);
 | 
			
		||||
    EXPECT_EQ(res, fmt::format("{} {}", request, GetParam().expectedResponse));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -503,27 +542,27 @@ INSTANTIATE_TEST_CASE_P(
 | 
			
		||||
    WebServerAdminTest,
 | 
			
		||||
    ::testing::Values(
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword,
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {},
 | 
			
		||||
            .expectedResponse = "user"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword,
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {WebHeader(http::field::authorization, "")},
 | 
			
		||||
            .expectedResponse = "user"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword,
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {WebHeader(http::field::authorization, "s")},
 | 
			
		||||
            .expectedResponse = "user"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword,
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {WebHeader(http::field::authorization, SecertSha256)},
 | 
			
		||||
            .expectedResponse = "user"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword,
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {WebHeader(
 | 
			
		||||
                http::field::authorization,
 | 
			
		||||
                fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
 | 
			
		||||
@@ -531,12 +570,12 @@ INSTANTIATE_TEST_CASE_P(
 | 
			
		||||
            .expectedResponse = "admin"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse,
 | 
			
		||||
            .config = JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {WebHeader(http::field::authorization, SecertSha256)},
 | 
			
		||||
            .expectedResponse = "user"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse,
 | 
			
		||||
            .config = JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {WebHeader(
 | 
			
		||||
                http::field::authorization,
 | 
			
		||||
                fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
 | 
			
		||||
@@ -544,16 +583,20 @@ INSTANTIATE_TEST_CASE_P(
 | 
			
		||||
            .expectedResponse = "admin"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword,
 | 
			
		||||
            .config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {WebHeader(
 | 
			
		||||
                http::field::authentication_info,
 | 
			
		||||
                fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
 | 
			
		||||
            )},
 | 
			
		||||
            .expectedResponse = "user"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{.config = JSONServerConfigWithLocalAdmin, .headers = {}, .expectedResponse = "admin"},
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithNoSpecifiedAdmin,
 | 
			
		||||
            .config = JSONServerConfigWithLocalAdmin(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {},
 | 
			
		||||
            .expectedResponse = "admin"
 | 
			
		||||
        },
 | 
			
		||||
        WebServerAdminTestParams{
 | 
			
		||||
            .config = JSONServerConfigWithNoSpecifiedAdmin(tests::util::generateFreePort()),
 | 
			
		||||
            .headers = {},
 | 
			
		||||
            .expectedResponse = "admin"
 | 
			
		||||
        }
 | 
			
		||||
@@ -563,36 +606,40 @@ INSTANTIATE_TEST_CASE_P(
 | 
			
		||||
 | 
			
		||||
TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminSet)
 | 
			
		||||
{
 | 
			
		||||
    static auto constexpr JSONServerConfigWithBothAdminPasswordAndLocalAdmin = R"JSON(
 | 
			
		||||
        {
 | 
			
		||||
            "server":{
 | 
			
		||||
    uint32_t webServerPort = tests::util::generateFreePort();
 | 
			
		||||
    std::string JSONServerConfigWithBothAdminPasswordAndLocalAdmin = fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
        "server":{{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": 8888,
 | 
			
		||||
                "port": {},
 | 
			
		||||
                "admin_password": "secret",
 | 
			
		||||
                "local_admin": true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    )JSON";
 | 
			
		||||
            }}
 | 
			
		||||
    }})JSON",
 | 
			
		||||
        webServerPort
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    auto e = std::make_shared<AdminCheckExecutor>();
 | 
			
		||||
    Config const serverConfig{boost::json::parse(JSONServerConfigWithBothAdminPasswordAndLocalAdmin)};
 | 
			
		||||
    Config const serverConfig{parse(JSONServerConfigWithBothAdminPasswordAndLocalAdmin)};
 | 
			
		||||
    EXPECT_THROW(web::make_HttpServer(serverConfig, ctx, std::nullopt, dosGuardOverload, e), std::logic_error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminFalse)
 | 
			
		||||
{
 | 
			
		||||
    static auto constexpr JSONServerConfigWithNoAdminPasswordAndLocalAdminFalse = R"JSON(
 | 
			
		||||
        {
 | 
			
		||||
            "server":{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": 8888,
 | 
			
		||||
                "local_admin": false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    )JSON";
 | 
			
		||||
    uint32_t webServerPort = tests::util::generateFreePort();
 | 
			
		||||
    std::string JSONServerConfigWithNoAdminPasswordAndLocalAdminFalse = fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
        "server": {{
 | 
			
		||||
            "ip": "0.0.0.0",
 | 
			
		||||
            "port": {},
 | 
			
		||||
            "local_admin": false
 | 
			
		||||
        }}
 | 
			
		||||
    }})JSON",
 | 
			
		||||
        webServerPort
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    auto e = std::make_shared<AdminCheckExecutor>();
 | 
			
		||||
    Config const serverConfig{boost::json::parse(JSONServerConfigWithNoAdminPasswordAndLocalAdminFalse)};
 | 
			
		||||
    Config const serverConfig{parse(JSONServerConfigWithNoAdminPasswordAndLocalAdminFalse)};
 | 
			
		||||
    EXPECT_THROW(web::make_HttpServer(serverConfig, ctx, std::nullopt, dosGuardOverload, e), std::logic_error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -601,32 +648,35 @@ struct WebServerPrometheusTest : util::prometheus::WithPrometheus, WebServerTest
 | 
			
		||||
TEST_F(WebServerPrometheusTest, rejectedWithoutAdminPassword)
 | 
			
		||||
{
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword)};
 | 
			
		||||
    uint32_t webServerPort = tests::util::generateFreePort();
 | 
			
		||||
    Config const serverConfig{parse(JSONServerConfigWithAdminPassword(webServerPort))};
 | 
			
		||||
    auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
 | 
			
		||||
    auto const res = HttpSyncClient::syncGet("localhost", "8888", "", "/metrics");
 | 
			
		||||
    auto const res = HttpSyncClient::syncGet("localhost", std::to_string(webServerPort), "", "/metrics");
 | 
			
		||||
    EXPECT_EQ(res, "Only admin is allowed to collect metrics");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
 | 
			
		||||
{
 | 
			
		||||
    static auto constexpr JSONServerConfigWithDisabledPrometheus = R"JSON(
 | 
			
		||||
        {
 | 
			
		||||
            "server": {
 | 
			
		||||
    uint32_t webServerPort = tests::util::generateFreePort();
 | 
			
		||||
    std::string JSONServerConfigWithDisabledPrometheus = fmt::format(
 | 
			
		||||
        R"JSON({{
 | 
			
		||||
        "server":{{
 | 
			
		||||
                "ip": "0.0.0.0",
 | 
			
		||||
                "port": 8888,
 | 
			
		||||
                "port": {},
 | 
			
		||||
                "admin_password": "secret"
 | 
			
		||||
            },
 | 
			
		||||
            "prometheus": { "enabled": false }
 | 
			
		||||
        }
 | 
			
		||||
    )JSON";
 | 
			
		||||
            }},
 | 
			
		||||
        "prometheus": {{ "enabled": false }}
 | 
			
		||||
    }})JSON",
 | 
			
		||||
        webServerPort
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    Config const serverConfig{boost::json::parse(JSONServerConfigWithDisabledPrometheus)};
 | 
			
		||||
    Config const serverConfig{parse(JSONServerConfigWithDisabledPrometheus)};
 | 
			
		||||
    PrometheusService::init(serverConfig);
 | 
			
		||||
    auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
 | 
			
		||||
    auto const res = HttpSyncClient::syncGet(
 | 
			
		||||
        "localhost",
 | 
			
		||||
        "8888",
 | 
			
		||||
        std::to_string(webServerPort),
 | 
			
		||||
        "",
 | 
			
		||||
        "/metrics",
 | 
			
		||||
        {WebHeader(
 | 
			
		||||
@@ -639,14 +689,15 @@ TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
 | 
			
		||||
 | 
			
		||||
TEST_F(WebServerPrometheusTest, validResponse)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t webServerPort = tests::util::generateFreePort();
 | 
			
		||||
    auto& testCounter = PrometheusService::counterInt("test_counter", util::prometheus::Labels());
 | 
			
		||||
    ++testCounter;
 | 
			
		||||
    auto e = std::make_shared<EchoExecutor>();
 | 
			
		||||
    Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword)};
 | 
			
		||||
    Config const serverConfig{parse(JSONServerConfigWithAdminPassword(webServerPort))};
 | 
			
		||||
    auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
 | 
			
		||||
    auto const res = HttpSyncClient::syncGet(
 | 
			
		||||
        "localhost",
 | 
			
		||||
        "8888",
 | 
			
		||||
        std::to_string(webServerPort),
 | 
			
		||||
        "",
 | 
			
		||||
        "/metrics",
 | 
			
		||||
        {WebHeader(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user