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()