diff --git a/Makefile.am b/Makefile.am index 02f64100..0858ebc6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,10 +63,16 @@ check_PROGRAMS = test/libbitcoin-server-test test_libbitcoin_server_test_CPPFLAGS = -I${srcdir}/include ${bitcoin_node_BUILD_CPPFLAGS} test_libbitcoin_server_test_LDADD = src/libbitcoin-server.la ${boost_unit_test_framework_LIBS} ${bitcoin_node_LIBS} test_libbitcoin_server_test_SOURCES = \ + test/configuration.cpp \ test/error.cpp \ test/main.cpp \ + test/settings.cpp \ test/test.cpp \ - test/test.hpp + test/test.hpp \ + test/parsers/bitcoind_query.cpp \ + test/parsers/bitcoind_target.cpp \ + test/parsers/explore_query.cpp \ + test/parsers/explore_target.cpp endif WITH_TESTS diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index cd9022bd..c5084fbd 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -286,10 +286,16 @@ target_link_libraries( ${CANONICAL_LIB_NAME} #------------------------------------------------------------------------------ if (with-tests) add_executable( libbitcoin-server-test + "../../test/configuration.cpp" "../../test/error.cpp" "../../test/main.cpp" + "../../test/settings.cpp" "../../test/test.cpp" - "../../test/test.hpp" ) + "../../test/test.hpp" + "../../test/parsers/bitcoind_query.cpp" + "../../test/parsers/bitcoind_target.cpp" + "../../test/parsers/explore_query.cpp" + "../../test/parsers/explore_target.cpp" ) add_test( NAME libbitcoin-server-test COMMAND libbitcoin-server-test --run_test=* diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj index 8ec954d9..41bb11b5 100644 --- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj @@ -118,8 +118,14 @@ + + + + + + diff --git a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters index 11465d9a..5641fe98 100644 --- a/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters @@ -10,14 +10,35 @@ {66A0E586-2E3A-448F-0000-000000000000} + + {66A0E586-2E3A-448F-0000-000000000001} + + + src + src src + + src\parsers + + + src\parsers + + + src\parsers + + + src\parsers + + + src + src diff --git a/test/configuration.cpp b/test/configuration.cpp new file mode 100644 index 00000000..90b2a1f8 --- /dev/null +++ b/test/configuration.cpp @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "test.hpp" + +BOOST_AUTO_TEST_SUITE(configuration_tests) + +BOOST_AUTO_TEST_CASE(configuration__construct1__none_context__expected) +{ + const settings::embedded_pages web{}; + const settings::embedded_pages explorer{}; + const configuration instance(system::chain::selection::none, explorer, web); + + BOOST_REQUIRE(instance.file.empty()); + BOOST_REQUIRE(!instance.help); + BOOST_REQUIRE(!instance.hardware); + BOOST_REQUIRE(!instance.settings); + BOOST_REQUIRE(!instance.version); + BOOST_REQUIRE(!instance.newstore); + BOOST_REQUIRE(!instance.backup); + BOOST_REQUIRE(!instance.restore); + BOOST_REQUIRE(!instance.flags); + BOOST_REQUIRE(!instance.information); + BOOST_REQUIRE(!instance.slabs); + BOOST_REQUIRE(!instance.buckets); + BOOST_REQUIRE(!instance.collisions); + BOOST_REQUIRE_EQUAL(instance.test, system::null_hash); + BOOST_REQUIRE_EQUAL(instance.write, system::null_hash); + + // Just a sample of settings. + BOOST_REQUIRE(instance.node.headers_first); + BOOST_REQUIRE_EQUAL(instance.network.threads, 1_u32); + BOOST_REQUIRE_EQUAL(instance.bitcoin.first_version, 1_u32); + BOOST_REQUIRE_EQUAL(instance.log.application, network::levels::application_defined); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/parsers/bitcoind_query.cpp b/test/parsers/bitcoind_query.cpp new file mode 100644 index 00000000..23d806c3 --- /dev/null +++ b/test/parsers/bitcoind_query.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" + +BOOST_AUTO_TEST_SUITE(bitcoind_query_tests) + +BOOST_AUTO_TEST_CASE(bitcoind_query_test) +{ + BOOST_REQUIRE(true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/parsers/bitcoind_target.cpp b/test/parsers/bitcoind_target.cpp new file mode 100644 index 00000000..e77d9f49 --- /dev/null +++ b/test/parsers/bitcoind_target.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" + +BOOST_AUTO_TEST_SUITE(bitcoind_target_tests) + +BOOST_AUTO_TEST_CASE(bitcoind_target_test) +{ + BOOST_REQUIRE(true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/parsers/explore_query.cpp b/test/parsers/explore_query.cpp new file mode 100644 index 00000000..1418e666 --- /dev/null +++ b/test/parsers/explore_query.cpp @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" + +BOOST_AUTO_TEST_SUITE(explore_query_tests) + +using namespace network::http; + +BOOST_AUTO_TEST_CASE(parsers__explore_query__empty__false) +{ + network::rpc::request_t out{}; + BOOST_REQUIRE(!explore_query(out, network::http::request{})); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/parsers/explore_target.cpp b/test/parsers/explore_target.cpp new file mode 100644 index 00000000..6e0d8208 --- /dev/null +++ b/test/parsers/explore_target.cpp @@ -0,0 +1,1289 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" + +BOOST_AUTO_TEST_SUITE(explore_target_tests) + +using namespace system; +using namespace network::rpc; +using object_t = network::rpc::object_t; + +// General errors + +BOOST_AUTO_TEST_CASE(parsers__explore_target__empty_path__empty_path) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "?foo=bar"), server::error::empty_path); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__missing_version__missing_version) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/"), server::error::missing_version); + BOOST_REQUIRE_EQUAL(explore_target(out, "/block/height/123"), server::error::missing_version); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__invalid_version__invalid_number) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/vinvalid/block/height/123"), server::error::invalid_number); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__version_leading_zero__invalid_number) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v01/block/height/123"), server::error::invalid_number); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__missing_target__missing_target) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3"), server::error::missing_target); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__invalid_target__invalid_target) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/invalid"), server::error::invalid_target); +} + +// block/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_height_valid__expected) +{ + const std::string path = "/v42/block/height/123456"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_height_missing_height__missing_height) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height"), server::error::missing_height); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_height_invalid_height__invalid_number) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/invalid"), server::error::invalid_number); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_height_invalid_component__invalid_component) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/invalid"), server::error::invalid_component); +} + +// block/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_hash_valid__expected) +{ + const std::string path = "//v42//block//hash//0000000000000000000000000000000000000000000000000000000000000042//?foo=bar"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_hash_missing_hash__missing_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash"), server::error::missing_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_hash_invalid_hash__invalid_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/invalidhex"), server::error::invalid_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_hash_invalid_component__invalid_component) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_component); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_invalid_id_type__invalid_id_type) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/invalid/123"), server::error::invalid_id_type); +} + +// block_header/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/header/")); + BOOST_REQUIRE_EQUAL(request.method, "block_header"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_height_invalid_subcomponent__invalid_subcomponent) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/header/invalid"), server::error::invalid_subcomponent); +} + +// block_header/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_hash_valid__expected) +{ + const std::string path = "v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/header"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_header"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_hash_invalid_subcomponent__invalid_subcomponent) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/header/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_subcomponent); +} + +// block_header_context/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_context_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/header/context")); + BOOST_REQUIRE_EQUAL(request.method, "block_header_context"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_context_height_extra_segment__extra_segment) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/header/context/extra"), server::error::extra_segment); +} + +// block_header_context/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_context_hash_valid__expected) +{ + const std::string path = "v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/header/context"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_header_context"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_header_context_hash_extra_segment__extra_segment) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/header/context/extra"), server::error::extra_segment); +} + +// block_txs/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_txs_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/txs")); + BOOST_REQUIRE_EQUAL(request.method, "block_txs"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_txs_height_extra_segment__extra_segment) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/txs/extra"), server::error::extra_segment); +} + +// block_txs/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_txs_hash_valid__expected) +{ + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/txs"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_txs"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_txs_hash_extra_segment__extra_segment) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/txs/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// block_tx/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/tx/7")); + BOOST_REQUIRE_EQUAL(request.method, "block_tx"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); + + const auto position = std::get(object.at("position").value()); + BOOST_REQUIRE_EQUAL(position, 7u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_height_missing_position__missing_position) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/tx"), server::error::missing_position); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_height_invalid_position__invalid_number) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/tx/invalid"), server::error::invalid_number); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_height_extra_segment__extra_segment) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/tx/7/extra"), server::error::extra_segment); +} + +// block_tx/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_hash_valid__expected) +{ + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/tx/7"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_tx"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto position = std::get(object.at("position").value()); + BOOST_REQUIRE_EQUAL(position, 7u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_hash_missing_position__missing_position) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx"), server::error::missing_position); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_hash_invalid_position__invalid_number) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx/invalid"), server::error::invalid_number); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_tx_hash_extra_segment__extra_segment) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/tx/7/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// tx + +BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_valid__expected) +{ + const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "tx"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_missing_hash__missing_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/tx"), server::error::missing_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__transaction_invalid_hash__invalid_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/tx/invalidhex"), server::error::invalid_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_invalid_component__invalid_component) +{ + const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_component); +} + +// tx_header + +BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_header_valid__expected) +{ + const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042/header"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "tx_header"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_header_invalid_component__invalid_component) +{ + const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_component); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_header_extra_segment__extra_segment) +{ + const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/header/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// inputs + +BOOST_AUTO_TEST_CASE(parsers__explore_target__inputs_valid__expected) +{ + const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "inputs"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__inputs_missing_hash__missing_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/input"), server::error::missing_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__inputs_invalid_hash__invalid_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/input/invalidhex"), server::error::invalid_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__inputs_invalid_number__invalid_number) +{ + const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_number); +} + +// input + +BOOST_AUTO_TEST_CASE(parsers__explore_target__input_valid__expected) +{ + const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042/3"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "input"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto index = std::get(object.at("index").value()); + BOOST_REQUIRE_EQUAL(index, 3u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__input_missing_hash__missing_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/input"), server::error::missing_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__input_invalid_hash__invalid_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/input/invalidhex/3"), server::error::invalid_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__input_invalid_number__invalid_number) +{ + const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_number); +} + +// input_script + +BOOST_AUTO_TEST_CASE(parsers__explore_target__input_script_valid__expected) +{ + const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042/3/script"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "input_script"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto index = std::get(object.at("index").value()); + BOOST_REQUIRE_EQUAL(index, 3u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__input_script_extra_segment__extra_segment) +{ + const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/3/script/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// input_witness + +BOOST_AUTO_TEST_CASE(parsers__explore_target__input_witness_valid__expected) +{ + const std::string path = "/v255/input/0000000000000000000000000000000000000000000000000000000000000042/3/witness"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "input_witness"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto index = std::get(object.at("index").value()); + BOOST_REQUIRE_EQUAL(index, 3u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__input_witness_extra_segment__extra_segment) +{ + const std::string path = "/v3/input/0000000000000000000000000000000000000000000000000000000000000000/3/witness/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// outputs + +BOOST_AUTO_TEST_CASE(parsers__explore_target__outputs_valid__expected) +{ + const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "outputs"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__outputs_missing_hash__missing_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/output"), server::error::missing_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__outputs_invalid_hash__invalid_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/output/invalidhex"), server::error::invalid_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__outputs_invalid_number__invalid_number) +{ + const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_number); +} + +// output + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_valid__expected) +{ + const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/3"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "output"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto index = std::get(object.at("index").value()); + BOOST_REQUIRE_EQUAL(index, 3u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_invalid_number__invalid_number) +{ + const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_number); +} + +// output_script + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_script_valid__expected) +{ + const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/3/script"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "output_script"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto index = std::get(object.at("index").value()); + BOOST_REQUIRE_EQUAL(index, 3u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_script_invalid_subcomponent__invalid_subcomponent) +{ + const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/3/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_subcomponent); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_script_extra_segment__extra_segment) +{ + const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/3/script/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// output_spender + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_spender_valid__expected) +{ + const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/3/spender"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "output_spender"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto index = std::get(object.at("index").value()); + BOOST_REQUIRE_EQUAL(index, 3u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_spender_extra_segment__extra_segment) +{ + const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/3/spender/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// output_spenders + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_spenders_valid__expected) +{ + const std::string path = "/v255/output/0000000000000000000000000000000000000000000000000000000000000042/1/spenders"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "output_spenders"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto index = std::get(object.at("index").value()); + BOOST_REQUIRE_EQUAL(index, 1u); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__output_spenders_extra_segment__extra_segment) +{ + const std::string path = "/v3/output/0000000000000000000000000000000000000000000000000000000000000000/1/spenders/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// address + +BOOST_AUTO_TEST_CASE(parsers__explore_target__address_valid__reversed_expected) +{ + const std::string path = "/v255/address/0000000000000000000000000000000000000000000000000000000000000042"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "address"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 255u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__address_missing_hash__missing_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/address"), server::error::missing_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__address_invalid_hash__invalid_hash) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/address/invalidhex"), server::error::invalid_hash); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__address_invalid_subcomponent__invalid_subcomponent) +{ + const std::string path = "/v3/address/0000000000000000000000000000000000000000000000000000000000000000/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_subcomponent); +} + +// TODO: +// address/confirmed +// address/unconfirmed +// address/balance + +// block_filter/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!explore_target(request, "v42/block/height/123456/filter/255")); + BOOST_REQUIRE_EQUAL(request.method, "block_filter"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); + + const auto type = std::get(object.at("type").value()); + BOOST_REQUIRE_EQUAL(type, 255u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_height_invalid_subcomponent__invalid_subcomponent) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/42/invalid"), server::error::invalid_subcomponent); +} + +// block_filter/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_valid__expected) +{ + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_filter"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto type = std::get(object.at("type").value()); + BOOST_REQUIRE_EQUAL(type, 255u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_invalid_subcomponent__invalid_subcomponent) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/invalid"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::invalid_subcomponent); +} + +// block_filter_hash/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/filter/255/hash")); + BOOST_REQUIRE_EQUAL(request.method, "block_filter_hash"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); + + const auto type = std::get(object.at("type").value()); + BOOST_REQUIRE_EQUAL(type, 255u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_height_extra_segment__extra_segment) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/42/hash/extra"), server::error::extra_segment); +} + +// block_filter_hash/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_hash_valid__expected) +{ + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255/hash"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_filter_hash"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto type = std::get(object.at("type").value()); + BOOST_REQUIRE_EQUAL(type, 255u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_hash_hash_extra_segment__extra_segment) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/hash/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// block_filter_header/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_header_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/filter/255/header")); + BOOST_REQUIRE_EQUAL(request.method, "block_filter_header"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); + + const auto type = std::get(object.at("type").value()); + BOOST_REQUIRE_EQUAL(type, 255u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_header_height_extra_segment__extra_segment) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/42/header/extra"), server::error::extra_segment); +} + +// block_filter_header/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_header_hash_valid__expected) +{ + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/filter/255/header"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_filter_header"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 3u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); + + const auto type = std::get(object.at("type").value()); + BOOST_REQUIRE_EQUAL(type, 255u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_header_hash_extra_segment__extra_segment) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/header/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_missing_type_id__missing_type_id) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter"), server::error::missing_type_id); + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter"), server::error::missing_type_id); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_invalid_type__invalid_number) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/invalid"), server::error::invalid_number); + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/invalid"), server::error::invalid_number); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_filter_invalid_subcomponent__invalid_subcomponent) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/filter/42/invalid"), server::error::invalid_subcomponent); + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/filter/42/invalid"), server::error::invalid_subcomponent); +} + +// tx_details + +BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_details_valid__expected) +{ + const std::string path = "/v42/tx/0000000000000000000000000000000000000000000000000000000000000042/details"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "tx_details"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__tx_details_extra_segment__extra_segment) +{ + const std::string path = "/v3/tx/0000000000000000000000000000000000000000000000000000000000000000/details/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + +// block_details/height + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_details_height_valid__expected) +{ + request_t request{}; + BOOST_REQUIRE(!explore_target(request, "/v42/block/height/123456/details")); + BOOST_REQUIRE_EQUAL(request.method, "block_details"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto height = std::get(object.at("height").value()); + BOOST_REQUIRE_EQUAL(height, 123456u); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_details_height_extra_segment__extra_segment) +{ + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, "/v3/block/height/123/details/extra"), server::error::extra_segment); +} + +// block_details/hash + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_details_hash_valid__expected) +{ + const std::string path = "/v42/block/hash/0000000000000000000000000000000000000000000000000000000000000042/details"; + + request_t request{}; + BOOST_REQUIRE(!explore_target(request, path)); + BOOST_REQUIRE_EQUAL(request.method, "block_details"); + BOOST_REQUIRE(request.params.has_value()); + + const auto& params = request.params.value(); + BOOST_REQUIRE(std::holds_alternative(params)); + + const auto& object = std::get(request.params.value()); + BOOST_REQUIRE_EQUAL(object.size(), 2u); + + const auto version = std::get(object.at("version").value()); + BOOST_REQUIRE_EQUAL(version, 42u); + + const auto& any = std::get(object.at("hash").value()); + BOOST_REQUIRE(any.holds_alternative()); + + const auto& hash_cptr = any.get(); + BOOST_REQUIRE(hash_cptr); + BOOST_REQUIRE_EQUAL(to_uintx(*hash_cptr), uint256_t{ 0x42 }); +} + +BOOST_AUTO_TEST_CASE(parsers__explore_target__block_details_hash_extra_segment__extra_segment) +{ + const std::string path = "/v3/block/hash/0000000000000000000000000000000000000000000000000000000000000000/details/extra"; + request_t out{}; + BOOST_REQUIRE_EQUAL(explore_target(out, path), server::error::extra_segment); +} + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/settings.cpp b/test/settings.cpp new file mode 100644 index 00000000..34278c52 --- /dev/null +++ b/test/settings.cpp @@ -0,0 +1,238 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "test.hpp" + +BOOST_AUTO_TEST_SUITE(settings_tests) + +using namespace bc::network; +using namespace bc::system::chain; + +// [log] + +BOOST_AUTO_TEST_CASE(settings__log__default_context__expected) +{ + const log::settings log{}; + BOOST_REQUIRE_EQUAL(log.application, levels::application_defined); + BOOST_REQUIRE_EQUAL(log.news, levels::news_defined); + BOOST_REQUIRE_EQUAL(log.session, levels::session_defined); + BOOST_REQUIRE_EQUAL(log.protocol, false /*levels::protocol_defined*/); + BOOST_REQUIRE_EQUAL(log.proxy, false /*levels::proxy_defined*/); + BOOST_REQUIRE_EQUAL(log.remote, levels::remote_defined); + BOOST_REQUIRE_EQUAL(log.fault, levels::fault_defined); + BOOST_REQUIRE_EQUAL(log.quitting, false /*levels::quitting_defined*/); + BOOST_REQUIRE_EQUAL(log.objects, false /*levels::objects_defined*/); + BOOST_REQUIRE_EQUAL(log.verbose, false /*levels::verbose_defined*/); + BOOST_REQUIRE_EQUAL(log.maximum_size, 1'000'000_u32); + BOOST_REQUIRE_EQUAL(log.path, ""); + BOOST_REQUIRE_EQUAL(log.log_file1(), "bs_end.log"); + BOOST_REQUIRE_EQUAL(log.log_file2(), "bs_begin.log"); + BOOST_REQUIRE_EQUAL(log.events_file(), "events.log"); +#if defined(HAVE_MSC) + BOOST_REQUIRE_EQUAL(log.symbols, ""); +#endif +} + +// [server] + +BOOST_AUTO_TEST_CASE(server__html_server__defaults__expected) +{ + const auto undefined = server::settings::embedded_pages{}; + const server::settings::html_server instance{ "test", undefined }; + + // tcp_server + BOOST_REQUIRE_EQUAL(instance.name, "test"); + BOOST_REQUIRE(!instance.secure); + BOOST_REQUIRE(instance.binds.empty()); + BOOST_REQUIRE_EQUAL(instance.connections, 0u); + BOOST_REQUIRE_EQUAL(instance.inactivity_minutes, 10u); + BOOST_REQUIRE_EQUAL(instance.expiration_minutes, 60u); + BOOST_REQUIRE(!instance.enabled()); + BOOST_REQUIRE(instance.inactivity() == minutes(10)); + BOOST_REQUIRE(instance.expiration() == minutes(60)); + + // http_server + BOOST_REQUIRE_EQUAL(instance.server, "libbitcoin/4.0"); + BOOST_REQUIRE(instance.hosts.empty()); + BOOST_REQUIRE(instance.host_names().empty()); + + // html_server + BOOST_REQUIRE(!instance.pages.enabled()); + BOOST_REQUIRE(instance.pages.css().empty()); + BOOST_REQUIRE(instance.pages.html().empty()); + BOOST_REQUIRE(instance.pages.ecma().empty()); + BOOST_REQUIRE(instance.pages.font().empty()); + BOOST_REQUIRE(instance.pages.icon().empty()); + BOOST_REQUIRE(instance.websocket); + BOOST_REQUIRE(instance.path.empty()); + BOOST_REQUIRE_EQUAL(instance.default_, "index.html"); +} + +BOOST_AUTO_TEST_CASE(server__web_server__defaults__expected) +{ + const server::settings::embedded_pages web{}; + const server::settings::embedded_pages explorer{}; + const server::settings instance{ selection::none, explorer, web }; + const auto& server = instance.web; + + // tcp_server + BOOST_REQUIRE_EQUAL(server.name, "web"); + BOOST_REQUIRE(!server.secure); + BOOST_REQUIRE(server.binds.empty()); + BOOST_REQUIRE_EQUAL(server.connections, 0u); + BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); + BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); + BOOST_REQUIRE(!server.enabled()); + BOOST_REQUIRE(server.inactivity() == minutes(10)); + BOOST_REQUIRE(server.expiration() == minutes(60)); + + // http_server + BOOST_REQUIRE_EQUAL(server.server, "libbitcoin/4.0"); + BOOST_REQUIRE(server.hosts.empty()); + BOOST_REQUIRE(server.host_names().empty()); + + // html_server + BOOST_REQUIRE(!server.pages.enabled()); + BOOST_REQUIRE(server.pages.css().empty()); + BOOST_REQUIRE(server.pages.html().empty()); + BOOST_REQUIRE(server.pages.ecma().empty()); + BOOST_REQUIRE(server.pages.font().empty()); + BOOST_REQUIRE(server.pages.icon().empty()); + BOOST_REQUIRE(server.path.empty()); + BOOST_REQUIRE(server.websocket); + BOOST_REQUIRE_EQUAL(server.default_, "index.html"); +} + +BOOST_AUTO_TEST_CASE(server__explore_server__defaults__expected) +{ + const server::settings::embedded_pages web{}; + const server::settings::embedded_pages explorer{}; + const server::settings instance{ selection::none, explorer, web }; + const auto& server = instance.explore; + + // tcp_server + BOOST_REQUIRE_EQUAL(server.name, "explore"); + BOOST_REQUIRE(!server.secure); + BOOST_REQUIRE(server.binds.empty()); + BOOST_REQUIRE_EQUAL(server.connections, 0u); + BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); + BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); + BOOST_REQUIRE(!server.enabled()); + BOOST_REQUIRE(server.inactivity() == minutes(10)); + BOOST_REQUIRE(server.expiration() == minutes(60)); + + // http_server + BOOST_REQUIRE_EQUAL(server.server, "libbitcoin/4.0"); + BOOST_REQUIRE(server.hosts.empty()); + BOOST_REQUIRE(server.host_names().empty()); + + // html_server + BOOST_REQUIRE(!server.pages.enabled()); + BOOST_REQUIRE(server.pages.css().empty()); + BOOST_REQUIRE(server.pages.html().empty()); + BOOST_REQUIRE(server.pages.ecma().empty()); + BOOST_REQUIRE(server.pages.font().empty()); + BOOST_REQUIRE(server.pages.icon().empty()); + BOOST_REQUIRE(server.path.empty()); + BOOST_REQUIRE(server.websocket); + BOOST_REQUIRE_EQUAL(server.default_, "index.html"); +} + +// TODO: could add websocket under bitcoind as a custom property. +BOOST_AUTO_TEST_CASE(server__bitcoind_server__defaults__expected) +{ + const server::settings::embedded_pages web{}; + const server::settings::embedded_pages explorer{}; + const server::settings instance{ selection::none, explorer, web }; + const auto& server = instance.bitcoind; + + // tcp_server + BOOST_REQUIRE_EQUAL(server.name, "bitcoind"); + BOOST_REQUIRE(!server.secure); + BOOST_REQUIRE(server.binds.empty()); + BOOST_REQUIRE_EQUAL(server.connections, 0u); + BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); + BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); + BOOST_REQUIRE(!server.enabled()); + BOOST_REQUIRE(server.inactivity() == minutes(10)); + BOOST_REQUIRE(server.expiration() == minutes(60)); + + // http_server + BOOST_REQUIRE_EQUAL(server.server, "libbitcoin/4.0"); + BOOST_REQUIRE(server.hosts.empty()); + BOOST_REQUIRE(server.host_names().empty()); +} + +BOOST_AUTO_TEST_CASE(server__electrum_server__defaults__expected) +{ + const server::settings::embedded_pages web{}; + const server::settings::embedded_pages explorer{}; + const server::settings instance{ selection::none, explorer, web }; + const auto& server = instance.electrum; + + // tcp_server + BOOST_REQUIRE_EQUAL(server.name, "electrum"); + BOOST_REQUIRE(!server.secure); + BOOST_REQUIRE(server.binds.empty()); + BOOST_REQUIRE_EQUAL(server.connections, 0u); + BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); + BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); + BOOST_REQUIRE(!server.enabled()); + BOOST_REQUIRE(server.inactivity() == minutes(10)); + BOOST_REQUIRE(server.expiration() == minutes(60)); +} + +BOOST_AUTO_TEST_CASE(server__stratum_v1_server__defaults__expected) +{ + const server::settings::embedded_pages web{}; + const server::settings::embedded_pages explorer{}; + const server::settings instance{ selection::none, explorer, web }; + const auto& server = instance.stratum_v1; + + // tcp_server + BOOST_REQUIRE_EQUAL(server.name, "stratum_v1"); + BOOST_REQUIRE(!server.secure); + BOOST_REQUIRE(server.binds.empty()); + BOOST_REQUIRE_EQUAL(server.connections, 0u); + BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); + BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); + BOOST_REQUIRE(!server.enabled()); + BOOST_REQUIRE(server.inactivity() == minutes(10)); + BOOST_REQUIRE(server.expiration() == minutes(60)); +} + +BOOST_AUTO_TEST_CASE(server__stratum_v2_server__defaults__expected) +{ + const server::settings::embedded_pages web{}; + const server::settings::embedded_pages explorer{}; + const server::settings instance{ selection::none, explorer, web }; + const auto& server = instance.stratum_v2; + + // tcp_server + BOOST_REQUIRE_EQUAL(server.name, "stratum_v2"); + BOOST_REQUIRE(!server.secure); + BOOST_REQUIRE(server.binds.empty()); + BOOST_REQUIRE_EQUAL(server.connections, 0u); + BOOST_REQUIRE_EQUAL(server.inactivity_minutes, 10u); + BOOST_REQUIRE_EQUAL(server.expiration_minutes, 60u); + BOOST_REQUIRE(!server.enabled()); + BOOST_REQUIRE(server.inactivity() == minutes(10)); + BOOST_REQUIRE(server.expiration() == minutes(60)); +} + +BOOST_AUTO_TEST_SUITE_END()