From aef3119efbe7c5ecabfbd87e484c406048f235e3 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 21 May 2025 15:06:31 +0100 Subject: [PATCH 01/33] fix: Fix some doxygen docs errors (#2130) --- .github/workflows/docs.yml | 38 ++++++++++++++++---------- .pre-commit-config.yaml | 5 ++-- docs/Doxyfile | 1 + pre-commit-hooks/check-doxygen-docs.sh | 1 + src/etl/impl/SourceImpl.hpp | 1 - src/web/ng/impl/ErrorHandling.hpp | 2 +- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b9dc988d0..1e3488bde 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,23 +5,14 @@ on: branches: [develop] workflow_dispatch: -permissions: - contents: read - pages: write - id-token: write - concurrency: # Only cancel in-progress jobs or runs for the current workflow - matches against branch & tags group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} + build: runs-on: ubuntu-latest - continue-on-error: true container: image: ghcr.io/xrplf/clio-ci:latest @@ -31,10 +22,16 @@ jobs: with: lfs: true - - name: Build docs - run: | - mkdir -p build_docs && cd build_docs - cmake ../docs && cmake --build . --target docs + - name: Create build directory + run: mkdir build_docs + + - name: Configure CMake + working-directory: build_docs + run: cmake ../docs + + - name: Build + working-directory: build_docs + run: cmake --build . --target docs - name: Setup Pages uses: actions/configure-pages@v5 @@ -45,6 +42,19 @@ jobs: path: build_docs/html name: docs-develop + deploy: + needs: build + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + runs-on: ubuntu-latest + + steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8686de35e..df477e58c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,6 +11,8 @@ # # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks +exclude: ^docs/doxygen-awesome-theme/ + repos: # `pre-commit sample-config` default hooks - repo: https://github.com/pre-commit/pre-commit-hooks @@ -20,16 +22,13 @@ repos: - id: check-executables-have-shebangs - id: check-shebang-scripts-are-executable - id: end-of-file-fixer - exclude: ^docs/doxygen-awesome-theme/ - id: trailing-whitespace - exclude: ^docs/doxygen-awesome-theme/ # Autoformat: YAML, JSON, Markdown, etc. - repo: https://github.com/rbubley/mirrors-prettier rev: 787fb9f542b140ba0b2aced38e6a3e68021647a3 # frozen: v3.5.3 hooks: - id: prettier - exclude: ^docs/doxygen-awesome-theme/ - repo: https://github.com/igorshubovych/markdownlint-cli rev: 586c3ea3f51230da42bab657c6a32e9e66c364f0 # frozen: v0.44.0 diff --git a/docs/Doxyfile b/docs/Doxyfile index f9d897d63..cf0f1c71d 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -22,6 +22,7 @@ WARNINGS = ${LINT} WARN_NO_PARAMDOC = ${LINT} WARN_IF_INCOMPLETE_DOC = ${LINT} WARN_IF_UNDOCUMENTED = ${LINT} +WARN_AS_ERROR = ${WARN_AS_ERROR} GENERATE_LATEX = NO GENERATE_HTML = YES diff --git a/pre-commit-hooks/check-doxygen-docs.sh b/pre-commit-hooks/check-doxygen-docs.sh index 546fd259d..dce0b93de 100755 --- a/pre-commit-hooks/check-doxygen-docs.sh +++ b/pre-commit-hooks/check-doxygen-docs.sh @@ -61,6 +61,7 @@ pushd ${DOCDIR} > /dev/null 2>&1 cat ${ROOT}/docs/Doxyfile | \ sed \ -e "s/\${LINT}/YES/" \ + -e "s/\${WARN_AS_ERROR}/NO/" \ -e "s!\${SOURCE}!${ROOT}!" \ -e "s/\${USE_DOT}/NO/" \ -e "s/\${EXCLUDES}/impl/" \ diff --git a/src/etl/impl/SourceImpl.hpp b/src/etl/impl/SourceImpl.hpp index a2b4175e0..f48bd9f1c 100644 --- a/src/etl/impl/SourceImpl.hpp +++ b/src/etl/impl/SourceImpl.hpp @@ -198,7 +198,6 @@ public: * * @param sequence Sequence of the ledger to download * @param numMarkers Number of markers to generate for async calls - * @param cacheOnly Only insert into cache, not the DB; defaults to false * @return A std::pair of the data and a bool indicating whether the download was successful */ std::pair, bool> diff --git a/src/web/ng/impl/ErrorHandling.hpp b/src/web/ng/impl/ErrorHandling.hpp index 88b87639a..eff6c107d 100644 --- a/src/web/ng/impl/ErrorHandling.hpp +++ b/src/web/ng/impl/ErrorHandling.hpp @@ -93,7 +93,7 @@ public: makeJsonParsingError() const; /** - * @beirf Compose an error into json object from a status. + * @brief Compose an error into json object from a status. * * @param error The status to compose into a json object. * @return The composed json object. From 8aab33c18c3fde7a97ce54961866d0a25b7a2b17 Mon Sep 17 00:00:00 2001 From: Sergey Kuznetsov Date: Thu, 22 May 2025 13:43:52 +0100 Subject: [PATCH 02/33] fix: Add Delegate to Ledger types (#2151) Fix discrepancy with rippled for `account_objects` API. --- src/util/LedgerUtils.hpp | 1 + tests/unit/util/LedgerUtilsTests.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/util/LedgerUtils.hpp b/src/util/LedgerUtils.hpp index 6e01e3e0e..84a5606ba 100644 --- a/src/util/LedgerUtils.hpp +++ b/src/util/LedgerUtils.hpp @@ -118,6 +118,7 @@ class LedgerTypes { LedgerTypeAttribute::deletionBlockerLedgerType(JS(mpt_issuance), ripple::ltMPTOKEN_ISSUANCE), LedgerTypeAttribute::deletionBlockerLedgerType(JS(mptoken), ripple::ltMPTOKEN), LedgerTypeAttribute::deletionBlockerLedgerType(JS(permissioned_domain), ripple::ltPERMISSIONED_DOMAIN), + LedgerTypeAttribute::accountOwnedLedgerType(JS(delegate), ripple::ltDELEGATE), }; public: diff --git a/tests/unit/util/LedgerUtilsTests.cpp b/tests/unit/util/LedgerUtilsTests.cpp index 98a8a8d49..47318b148 100644 --- a/tests/unit/util/LedgerUtilsTests.cpp +++ b/tests/unit/util/LedgerUtilsTests.cpp @@ -57,7 +57,8 @@ TEST(LedgerUtilsTests, LedgerObjectTypeList) JS(permissioned_domain), JS(oracle), JS(credential), - JS(nunl) + JS(nunl), + JS(delegate) }; static_assert(std::size(kTYPES_LIST) == kTYPES.size()); @@ -91,6 +92,7 @@ TEST(LedgerUtilsTests, AccountOwnedTypeList) JS(mpt_issuance), JS(mptoken), JS(permissioned_domain), + JS(delegate) }; static_assert(std::size(kCORRECT_TYPES) == kACCOUNT_OWNED.size()); From 2a147b948724b9c545dfafc4a936aec3b7e53e13 Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Fri, 23 May 2025 15:01:50 +0100 Subject: [PATCH 03/33] feat: ETLng publisher and service refactoring (#2138) --- src/app/ClioApplication.cpp | 7 +- src/etl/CacheLoader.hpp | 17 +- src/etl/ETLService.cpp | 90 ++++ src/etl/ETLService.hpp | 34 +- src/etl/impl/LedgerPublisher.hpp | 12 +- src/etlng/CMakeLists.txt | 3 +- src/etlng/CacheLoaderInterface.hpp | 53 ++ src/etlng/CacheUpdaterInterface.hpp | 66 +++ src/etlng/ETLService.cpp | 275 +++++++++++ src/etlng/ETLService.hpp | 237 +++------ src/etlng/LedgerPublisherInterface.hpp | 27 +- src/etlng/MonitorInterface.hpp | 7 + src/etlng/TaskManagerInterface.hpp | 46 ++ src/etlng/TaskManagerProviderInterface.hpp | 51 ++ src/etlng/impl/AmendmentBlockHandler.cpp | 2 +- src/etlng/impl/AmendmentBlockHandler.hpp | 2 +- src/etlng/impl/CacheUpdater.hpp | 69 +++ src/etlng/impl/Extraction.hpp | 7 + src/etlng/impl/LedgerPublisher.hpp | 286 +++++++++++ src/etlng/impl/Loading.cpp | 52 +- src/etlng/impl/Loading.hpp | 9 +- src/etlng/impl/Monitor.cpp | 9 + src/etlng/impl/Monitor.hpp | 3 + src/etlng/impl/Registry.hpp | 54 +- src/etlng/impl/TaskManager.cpp | 73 ++- src/etlng/impl/TaskManager.hpp | 54 +- src/etlng/impl/TaskManagerProvider.hpp | 74 +++ .../impl/TaskQueue.hpp} | 96 ++-- src/etlng/impl/ext/Cache.cpp | 14 +- src/etlng/impl/ext/Cache.hpp | 16 +- tests/common/util/MockLedgerPublisher.hpp | 10 +- tests/unit/CMakeLists.txt | 3 +- tests/unit/etlng/ETLServiceTests.cpp | 341 +++++++++++++ tests/unit/etlng/LedgerPublisherTests.cpp | 357 ++++++++++++++ tests/unit/etlng/LoadingTests.cpp | 34 +- tests/unit/etlng/MonitorTests.cpp | 17 +- tests/unit/etlng/RegistryTests.cpp | 465 +++++++++++++++++- tests/unit/etlng/TaskManagerTests.cpp | 24 +- tests/unit/etlng/ext/CacheTests.cpp | 13 +- .../unit/util/StrandedPriorityQueueTests.cpp | 195 -------- 40 files changed, 2619 insertions(+), 585 deletions(-) create mode 100644 src/etlng/CacheLoaderInterface.hpp create mode 100644 src/etlng/CacheUpdaterInterface.hpp create mode 100644 src/etlng/ETLService.cpp create mode 100644 src/etlng/TaskManagerInterface.hpp create mode 100644 src/etlng/TaskManagerProviderInterface.hpp create mode 100644 src/etlng/impl/CacheUpdater.hpp create mode 100644 src/etlng/impl/LedgerPublisher.hpp create mode 100644 src/etlng/impl/TaskManagerProvider.hpp rename src/{util/StrandedPriorityQueue.hpp => etlng/impl/TaskQueue.hpp} (54%) create mode 100644 tests/unit/etlng/ETLServiceTests.cpp create mode 100644 tests/unit/etlng/LedgerPublisherTests.cpp delete mode 100644 tests/unit/util/StrandedPriorityQueueTests.cpp diff --git a/src/app/ClioApplication.cpp b/src/app/ClioApplication.cpp index 898c38172..903089d6b 100644 --- a/src/app/ClioApplication.cpp +++ b/src/app/ClioApplication.cpp @@ -36,6 +36,7 @@ #include "rpc/RPCEngine.hpp" #include "rpc/WorkQueue.hpp" #include "rpc/common/impl/HandlerProvider.hpp" +#include "util/async/context/BasicExecutionContext.hpp" #include "util/build/Build.hpp" #include "util/config/ConfigDefinition.hpp" #include "util/log/Logger.hpp" @@ -103,6 +104,10 @@ ClioApplication::run(bool const useNgWebServer) // This is not the only io context in the application. boost::asio::io_context ioc{threads}; + // Similarly we need a context to run ETLng on + // In the future we can remove the raw ioc and use ctx instead + util::async::CoroExecutionContext ctx{threads}; + // Rate limiter, to prevent abuse auto whitelistHandler = web::dosguard::WhitelistHandler{config_}; auto const dosguardWeights = web::dosguard::Weights::make(config_); @@ -146,7 +151,7 @@ ClioApplication::run(bool const useNgWebServer) }(); // ETL is responsible for writing and publishing to streams. In read-only mode, ETL only publishes - auto etl = etl::ETLService::makeETLService(config_, ioc, backend, subscriptions, balancer, ledgers); + auto etl = etl::ETLService::makeETLService(config_, ioc, ctx, backend, subscriptions, balancer, ledgers); auto workQueue = rpc::WorkQueue::makeWorkQueue(config_); auto counters = rpc::Counters::makeCounters(workQueue); diff --git a/src/etl/CacheLoader.hpp b/src/etl/CacheLoader.hpp index 3e6d4a077..feaf592a4 100644 --- a/src/etl/CacheLoader.hpp +++ b/src/etl/CacheLoader.hpp @@ -26,6 +26,7 @@ #include "etl/impl/CursorFromAccountProvider.hpp" #include "etl/impl/CursorFromDiffProvider.hpp" #include "etl/impl/CursorFromFixDiffNumProvider.hpp" +#include "etlng/CacheLoaderInterface.hpp" #include "util/Assert.hpp" #include "util/async/context/BasicExecutionContext.hpp" #include "util/log/Logger.hpp" @@ -33,6 +34,7 @@ #include #include #include +#include namespace etl { @@ -46,7 +48,7 @@ namespace etl { * @tparam ExecutionContextType The type of the execution context to use */ template -class CacheLoader { +class CacheLoader : public etlng::CacheLoaderInterface { using CacheLoaderType = impl::CacheLoaderImpl; util::Logger log_{"ETL"}; @@ -67,10 +69,13 @@ public: */ CacheLoader( util::config::ClioConfigDefinition const& config, - std::shared_ptr const& backend, + std::shared_ptr backend, data::LedgerCacheInterface& cache ) - : backend_{backend}, cache_{cache}, settings_{makeCacheLoaderSettings(config)}, ctx_{settings_.numThreads} + : backend_{std::move(backend)} + , cache_{cache} + , settings_{makeCacheLoaderSettings(config)} + , ctx_{settings_.numThreads} { } @@ -83,7 +88,7 @@ public: * @param seq The sequence number to load cache for */ void - load(uint32_t const seq) + load(uint32_t const seq) override { ASSERT(not cache_.get().isFull(), "Cache must not be full. seq = {}", seq); @@ -129,7 +134,7 @@ public: * @brief Requests the loader to stop asap */ void - stop() noexcept + stop() noexcept override { if (loader_ != nullptr) loader_->stop(); @@ -139,7 +144,7 @@ public: * @brief Waits for the loader to finish background work */ void - wait() noexcept + wait() noexcept override { if (loader_ != nullptr) loader_->wait(); diff --git a/src/etl/ETLService.cpp b/src/etl/ETLService.cpp index 6211e5ed1..ac1bec786 100644 --- a/src/etl/ETLService.cpp +++ b/src/etl/ETLService.cpp @@ -20,17 +20,38 @@ #include "etl/ETLService.hpp" #include "data/BackendInterface.hpp" +#include "etl/CacheLoader.hpp" #include "etl/CorruptionDetector.hpp" +#include "etl/ETLState.hpp" +#include "etl/LoadBalancer.hpp" #include "etl/NetworkValidatedLedgersInterface.hpp" +#include "etl/SystemState.hpp" +#include "etl/impl/AmendmentBlockHandler.hpp" +#include "etl/impl/ExtractionDataPipe.hpp" +#include "etl/impl/Extractor.hpp" +#include "etl/impl/LedgerFetcher.hpp" +#include "etl/impl/LedgerLoader.hpp" +#include "etl/impl/LedgerPublisher.hpp" +#include "etl/impl/Transformer.hpp" +#include "etlng/ETLService.hpp" +#include "etlng/ETLServiceInterface.hpp" +#include "etlng/LoadBalancer.hpp" #include "etlng/LoadBalancerInterface.hpp" +#include "etlng/impl/LedgerPublisher.hpp" +#include "etlng/impl/TaskManagerProvider.hpp" #include "feed/SubscriptionManagerInterface.hpp" #include "util/Assert.hpp" #include "util/Constants.hpp" +#include "util/async/AnyExecutionContext.hpp" #include "util/config/ConfigDefinition.hpp" #include "util/log/Logger.hpp" #include +#include +#include +#include #include +#include #include #include @@ -45,6 +66,75 @@ namespace etl { +std::shared_ptr +ETLService::makeETLService( + util::config::ClioConfigDefinition const& config, + boost::asio::io_context& ioc, + util::async::AnyExecutionContext ctx, + std::shared_ptr backend, + std::shared_ptr subscriptions, + std::shared_ptr balancer, + std::shared_ptr ledgers +) +{ + std::shared_ptr ret; + + if (config.get("__ng_etl")) { + ASSERT( + std::dynamic_pointer_cast(balancer), "LoadBalancer type must be etlng::LoadBalancer" + ); + + auto state = std::make_shared(); + + auto fetcher = std::make_shared(backend, balancer); + auto extractor = std::make_shared(fetcher); + auto publisher = std::make_shared(ioc, backend, subscriptions, *state); + auto cacheLoader = std::make_shared>(config, backend, backend->cache()); + auto cacheUpdater = std::make_shared(backend->cache()); + auto amendmentBlockHandler = std::make_shared(ctx, *state); + + auto loader = std::make_shared( + backend, + etlng::impl::makeRegistry( + *state, + etlng::impl::CacheExt{cacheUpdater}, + etlng::impl::CoreExt{backend}, + etlng::impl::SuccessorExt{backend, backend->cache()}, + etlng::impl::NFTExt{backend} + ), + amendmentBlockHandler + ); + + auto taskManagerProvider = std::make_shared(*ledgers, extractor, loader); + + ret = std::make_shared( + ctx, + config, + backend, + balancer, + ledgers, + publisher, + cacheLoader, + cacheUpdater, + extractor, + loader, // loader itself + loader, // initial load observer + taskManagerProvider, + state + ); + } else { + ASSERT(std::dynamic_pointer_cast(balancer), "LoadBalancer type must be etl::LoadBalancer"); + ret = std::make_shared(config, ioc, backend, subscriptions, balancer, ledgers); + } + + // inject networkID into subscriptions, as transaction feed require it to inject CTID in response + if (auto const state = ret->getETLState(); state) + subscriptions->setNetworkID(state->networkID); + + ret->run(); + return ret; +} + // Database must be populated when this starts std::optional ETLService::runETLPipeline(uint32_t startSequence, uint32_t numExtractors) diff --git a/src/etl/ETLService.hpp b/src/etl/ETLService.hpp index 964258dbf..af9a34429 100644 --- a/src/etl/ETLService.hpp +++ b/src/etl/ETLService.hpp @@ -22,7 +22,6 @@ #include "data/BackendInterface.hpp" #include "etl/CacheLoader.hpp" #include "etl/ETLState.hpp" -#include "etl/LoadBalancer.hpp" #include "etl/NetworkValidatedLedgersInterface.hpp" #include "etl/SystemState.hpp" #include "etl/impl/AmendmentBlockHandler.hpp" @@ -32,12 +31,12 @@ #include "etl/impl/LedgerLoader.hpp" #include "etl/impl/LedgerPublisher.hpp" #include "etl/impl/Transformer.hpp" -#include "etlng/ETLService.hpp" #include "etlng/ETLServiceInterface.hpp" -#include "etlng/LoadBalancer.hpp" #include "etlng/LoadBalancerInterface.hpp" +#include "etlng/impl/LedgerPublisher.hpp" +#include "etlng/impl/TaskManagerProvider.hpp" #include "feed/SubscriptionManagerInterface.hpp" -#include "util/Assert.hpp" +#include "util/async/AnyExecutionContext.hpp" #include "util/log/Logger.hpp" #include @@ -150,6 +149,7 @@ public: * * @param config The configuration to use * @param ioc io context to run on + * @param ctx Execution context for asynchronous operations * @param backend BackendInterface implementation * @param subscriptions Subscription manager * @param balancer Load balancer to use @@ -160,34 +160,12 @@ public: makeETLService( util::config::ClioConfigDefinition const& config, boost::asio::io_context& ioc, + util::async::AnyExecutionContext ctx, std::shared_ptr backend, std::shared_ptr subscriptions, std::shared_ptr balancer, std::shared_ptr ledgers - ) - { - std::shared_ptr ret; - - if (config.get("__ng_etl")) { - ASSERT( - std::dynamic_pointer_cast(balancer), - "LoadBalancer type must be etlng::LoadBalancer" - ); - ret = std::make_shared(config, backend, subscriptions, balancer, ledgers); - } else { - ASSERT( - std::dynamic_pointer_cast(balancer), "LoadBalancer type must be etl::LoadBalancer" - ); - ret = std::make_shared(config, ioc, backend, subscriptions, balancer, ledgers); - } - - // inject networkID into subscriptions, as transaction feed require it to inject CTID in response - if (auto const state = ret->getETLState(); state) - subscriptions->setNetworkID(state->networkID); - - ret->run(); - return ret; - } + ); /** * @brief Stops components and joins worker thread. diff --git a/src/etl/impl/LedgerPublisher.hpp b/src/etl/impl/LedgerPublisher.hpp index 4c21b4c21..e0960c5c1 100644 --- a/src/etl/impl/LedgerPublisher.hpp +++ b/src/etl/impl/LedgerPublisher.hpp @@ -24,6 +24,7 @@ #include "data/LedgerCacheInterface.hpp" #include "data/Types.hpp" #include "etl/SystemState.hpp" +#include "etlng/LedgerPublisherInterface.hpp" #include "feed/SubscriptionManagerInterface.hpp" #include "util/Assert.hpp" #include "util/log/Logger.hpp" @@ -31,6 +32,7 @@ #include "util/prometheus/Prometheus.hpp" #include +#include #include #include #include @@ -66,7 +68,7 @@ namespace etl::impl { * includes reading all of the transactions from the database) is done from the application wide asio io_service, and a * strand is used to ensure ledgers are published in order. */ -class LedgerPublisher { +class LedgerPublisher : public etlng::LedgerPublisherInterface { util::Logger log_{"ETL"}; boost::asio::strand publishStrand_; @@ -121,7 +123,7 @@ public: uint32_t ledgerSequence, std::optional maxAttempts, std::chrono::steady_clock::duration attemptsDelay = std::chrono::seconds{1} - ) + ) override { LOG(log_.info()) << "Attempting to publish ledger = " << ledgerSequence; size_t numAttempts = 0; @@ -235,7 +237,7 @@ public: * @brief Get time passed since last publish, in seconds */ std::uint32_t - lastPublishAgeSeconds() const + lastPublishAgeSeconds() const override { return std::chrono::duration_cast(std::chrono::system_clock::now() - getLastPublish()) .count(); @@ -245,7 +247,7 @@ public: * @brief Get last publish time as a time point */ std::chrono::time_point - getLastPublish() const + getLastPublish() const override { return std::chrono::time_point{std::chrono::seconds{lastPublishSeconds_.get().value() }}; @@ -255,7 +257,7 @@ public: * @brief Get time passed since last ledger close, in seconds */ std::uint32_t - lastCloseAgeSeconds() const + lastCloseAgeSeconds() const override { std::shared_lock const lck(closeTimeMtx_); auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) diff --git a/src/etlng/CMakeLists.txt b/src/etlng/CMakeLists.txt index 49e443a34..d0640b545 100644 --- a/src/etlng/CMakeLists.txt +++ b/src/etlng/CMakeLists.txt @@ -2,7 +2,8 @@ add_library(clio_etlng) target_sources( clio_etlng - PRIVATE LoadBalancer.cpp + PRIVATE ETLService.cpp + LoadBalancer.cpp Source.cpp impl/AmendmentBlockHandler.cpp impl/AsyncGrpcCall.cpp diff --git a/src/etlng/CacheLoaderInterface.hpp b/src/etlng/CacheLoaderInterface.hpp new file mode 100644 index 000000000..e90adeff0 --- /dev/null +++ b/src/etlng/CacheLoaderInterface.hpp @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include + +namespace etlng { + +/** + * @brief An interface for the Cache Loader + */ +struct CacheLoaderInterface { + virtual ~CacheLoaderInterface() = default; + + /** + * @brief Load the cache with the most recent ledger data + * + * @param seq The sequence number of the ledger to load + */ + virtual void + load(uint32_t const seq) = 0; + + /** + * @brief Stop the cache loading process + */ + virtual void + stop() noexcept = 0; + + /** + * @brief Wait for all cache loading tasks to complete + */ + virtual void + wait() noexcept = 0; +}; + +} // namespace etlng diff --git a/src/etlng/CacheUpdaterInterface.hpp b/src/etlng/CacheUpdaterInterface.hpp new file mode 100644 index 000000000..dc31cea34 --- /dev/null +++ b/src/etlng/CacheUpdaterInterface.hpp @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include "data/Types.hpp" +#include "etlng/Models.hpp" + +#include +#include + +namespace etlng { + +/** + * @brief An interface for the Cache Updater + */ +struct CacheUpdaterInterface { + virtual ~CacheUpdaterInterface() = default; + + /** + * @brief Update the cache with ledger data + * @param data The ledger data to update with + */ + virtual void + update(model::LedgerData const& data) = 0; + + /** + * @brief Update the cache with ledger objects at a specific sequence + * @param seq The ledger sequence number + * @param objs The ledger objects to update with + */ + virtual void + update(uint32_t seq, std::vector const& objs) = 0; + + /** + * @brief Update the cache with model objects at a specific sequence + * @param seq The ledger sequence number + * @param objs The model objects to update with + */ + virtual void + update(uint32_t seq, std::vector const& objs) = 0; + + /** + * @brief Mark the cache as fully loaded + */ + virtual void + setFull() = 0; +}; + +} // namespace etlng diff --git a/src/etlng/ETLService.cpp b/src/etlng/ETLService.cpp new file mode 100644 index 000000000..0116f0a4c --- /dev/null +++ b/src/etlng/ETLService.cpp @@ -0,0 +1,275 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include "etlng/ETLService.hpp" + +#include "data/BackendInterface.hpp" +#include "data/LedgerCacheInterface.hpp" +#include "data/Types.hpp" +#include "etl/ETLState.hpp" +#include "etl/NetworkValidatedLedgersInterface.hpp" +#include "etl/SystemState.hpp" +#include "etl/impl/AmendmentBlockHandler.hpp" +#include "etl/impl/LedgerFetcher.hpp" +#include "etlng/CacheLoaderInterface.hpp" +#include "etlng/CacheUpdaterInterface.hpp" +#include "etlng/ExtractorInterface.hpp" +#include "etlng/InitialLoadObserverInterface.hpp" +#include "etlng/LedgerPublisherInterface.hpp" +#include "etlng/LoadBalancerInterface.hpp" +#include "etlng/LoaderInterface.hpp" +#include "etlng/MonitorInterface.hpp" +#include "etlng/TaskManagerProviderInterface.hpp" +#include "etlng/impl/AmendmentBlockHandler.hpp" +#include "etlng/impl/CacheUpdater.hpp" +#include "etlng/impl/Extraction.hpp" +#include "etlng/impl/LedgerPublisher.hpp" +#include "etlng/impl/Loading.hpp" +#include "etlng/impl/Monitor.hpp" +#include "etlng/impl/Registry.hpp" +#include "etlng/impl/Scheduling.hpp" +#include "etlng/impl/TaskManager.hpp" +#include "etlng/impl/ext/Cache.hpp" +#include "etlng/impl/ext/Core.hpp" +#include "etlng/impl/ext/NFT.hpp" +#include "etlng/impl/ext/Successor.hpp" +#include "util/Assert.hpp" +#include "util/Profiler.hpp" +#include "util/async/AnyExecutionContext.hpp" +#include "util/log/Logger.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace etlng { + +ETLService::ETLService( + util::async::AnyExecutionContext ctx, + std::reference_wrapper config, + std::shared_ptr backend, + std::shared_ptr balancer, + std::shared_ptr ledgers, + std::shared_ptr publisher, + std::shared_ptr cacheLoader, + std::shared_ptr cacheUpdater, + std::shared_ptr extractor, + std::shared_ptr loader, + std::shared_ptr initialLoadObserver, + std::shared_ptr taskManagerProvider, + std::shared_ptr state +) + : ctx_(std::move(ctx)) + , config_(config) + , backend_(std::move(backend)) + , balancer_(std::move(balancer)) + , ledgers_(std::move(ledgers)) + , publisher_(std::move(publisher)) + , cacheLoader_(std::move(cacheLoader)) + , cacheUpdater_(std::move(cacheUpdater)) + , extractor_(std::move(extractor)) + , loader_(std::move(loader)) + , initialLoadObserver_(std::move(initialLoadObserver)) + , taskManagerProvider_(std::move(taskManagerProvider)) + , state_(std::move(state)) +{ + LOG(log_.info()) << "Creating ETLng..."; +} + +ETLService::~ETLService() +{ + stop(); + LOG(log_.debug()) << "Destroying ETLng"; +} + +void +ETLService::run() +{ + LOG(log_.info()) << "Running ETLng..."; + + // TODO: write-enabled node should start in readonly and do the 10 second dance to become a writer + mainLoop_.emplace(ctx_.execute([this] { + state_->isWriting = + not state_->isReadOnly; // TODO: this is now needed because we don't have a mechanism for readonly or + // ETL writer node. remove later in favor of real mechanism + + auto const rng = loadInitialLedgerIfNeeded(); + + LOG(log_.info()) << "Waiting for next ledger to be validated by network..."; + std::optional const mostRecentValidated = ledgers_->getMostRecent(); + + if (not mostRecentValidated) { + LOG(log_.info()) << "The wait for the next validated ledger has been aborted. " + "Exiting monitor loop"; + return; + } + + ASSERT(rng.has_value(), "Ledger range can't be null"); + auto const nextSequence = rng->maxSequence + 1; + + LOG(log_.debug()) << "Database is populated. Starting monitor loop. sequence = " << nextSequence; + startMonitor(nextSequence); + + // TODO: we only want to run the full ETL task man if we are POSSIBLY a write node + // but definitely not in strict readonly + if (not state_->isReadOnly) + startLoading(nextSequence); + })); +} + +void +ETLService::stop() +{ + LOG(log_.info()) << "Stop called"; + + if (taskMan_) + taskMan_->stop(); + if (monitor_) + monitor_->stop(); +} + +boost::json::object +ETLService::getInfo() const +{ + boost::json::object result; + + result["etl_sources"] = balancer_->toJson(); + result["is_writer"] = static_cast(state_->isWriting); + result["read_only"] = static_cast(state_->isReadOnly); + auto last = publisher_->getLastPublish(); + if (last.time_since_epoch().count() != 0) + result["last_publish_age_seconds"] = std::to_string(publisher_->lastPublishAgeSeconds()); + return result; +} + +bool +ETLService::isAmendmentBlocked() const +{ + return state_->isAmendmentBlocked; +} + +bool +ETLService::isCorruptionDetected() const +{ + return state_->isCorruptionDetected; +} + +std::optional +ETLService::getETLState() const +{ + return balancer_->getETLState(); +} + +std::uint32_t +ETLService::lastCloseAgeSeconds() const +{ + return publisher_->lastCloseAgeSeconds(); +} + +std::optional +ETLService::loadInitialLedgerIfNeeded() +{ + auto rng = backend_->hardFetchLedgerRangeNoThrow(); + if (not rng.has_value()) { + LOG(log_.info()) << "Database is empty. Will download a ledger from the network."; + + LOG(log_.info()) << "Waiting for next ledger to be validated by network..."; + if (auto const mostRecentValidated = ledgers_->getMostRecent(); mostRecentValidated.has_value()) { + auto const seq = *mostRecentValidated; + LOG(log_.info()) << "Ledger " << seq << " has been validated. Downloading... "; + + auto [ledger, timeDiff] = ::util::timed>([this, seq]() { + return extractor_->extractLedgerOnly(seq).and_then([this, seq](auto&& data) { + // TODO: loadInitialLedger in balancer should be called fetchEdgeKeys or similar + data.edgeKeys = balancer_->loadInitialLedger(seq, *initialLoadObserver_); + + // TODO: this should be interruptible for graceful shutdown + return loader_->loadInitialLedger(data); + }); + }); + + if (not ledger.has_value()) { + LOG(log_.error()) << "Failed to load initial ledger. Exiting monitor loop"; + return std::nullopt; + } + + LOG(log_.debug()) << "Time to download and store ledger = " << timeDiff; + LOG(log_.info()) << "Finished loadInitialLedger. cache size = " << backend_->cache().size(); + + return backend_->hardFetchLedgerRangeNoThrow(); + } + + LOG(log_.info()) << "The wait for the next validated ledger has been aborted. " + "Exiting monitor loop"; + return std::nullopt; + } + + LOG(log_.info()) << "Database already populated. Picking up from the tip of history"; + cacheLoader_->load(rng->maxSequence); + + return rng; +} + +void +ETLService::startMonitor(uint32_t seq) +{ + monitor_ = std::make_unique(ctx_, backend_, ledgers_, seq); + monitorSubscription_ = monitor_->subscribe([this](uint32_t seq) { + log_.info() << "MONITOR got new seq from db: " << seq; + + // FIXME: is this the best way? + if (not state_->isWriting) { + auto const diff = data::synchronousAndRetryOnTimeout([this, seq](auto yield) { + return backend_->fetchLedgerDiff(seq, yield); + }); + cacheUpdater_->update(seq, diff); + } + + publisher_->publish(seq, {}); + }); + monitor_->run(); +} + +void +ETLService::startLoading(uint32_t seq) +{ + taskMan_ = taskManagerProvider_->make(ctx_, *monitor_, seq); + taskMan_->run(config_.get().get("extractor_threads")); +} + +} // namespace etlng diff --git a/src/etlng/ETLService.hpp b/src/etlng/ETLService.hpp index 5a54f14e6..168db6f90 100644 --- a/src/etlng/ETLService.hpp +++ b/src/etlng/ETLService.hpp @@ -20,22 +20,27 @@ #pragma once #include "data/BackendInterface.hpp" -#include "data/LedgerCache.hpp" #include "data/Types.hpp" -#include "etl/CacheLoader.hpp" #include "etl/ETLState.hpp" -#include "etl/LedgerFetcherInterface.hpp" #include "etl/NetworkValidatedLedgersInterface.hpp" #include "etl/SystemState.hpp" #include "etl/impl/AmendmentBlockHandler.hpp" #include "etl/impl/LedgerFetcher.hpp" -#include "etl/impl/LedgerPublisher.hpp" -#include "etlng/AmendmentBlockHandlerInterface.hpp" +#include "etlng/CacheLoaderInterface.hpp" +#include "etlng/CacheUpdaterInterface.hpp" #include "etlng/ETLServiceInterface.hpp" #include "etlng/ExtractorInterface.hpp" +#include "etlng/InitialLoadObserverInterface.hpp" +#include "etlng/LedgerPublisherInterface.hpp" #include "etlng/LoadBalancerInterface.hpp" +#include "etlng/LoaderInterface.hpp" +#include "etlng/MonitorInterface.hpp" +#include "etlng/TaskManagerInterface.hpp" +#include "etlng/TaskManagerProviderInterface.hpp" #include "etlng/impl/AmendmentBlockHandler.hpp" +#include "etlng/impl/CacheUpdater.hpp" #include "etlng/impl/Extraction.hpp" +#include "etlng/impl/LedgerPublisher.hpp" #include "etlng/impl/Loading.hpp" #include "etlng/impl/Monitor.hpp" #include "etlng/impl/Registry.hpp" @@ -45,14 +50,14 @@ #include "etlng/impl/ext/Core.hpp" #include "etlng/impl/ext/NFT.hpp" #include "etlng/impl/ext/Successor.hpp" -#include "feed/SubscriptionManagerInterface.hpp" -#include "util/Assert.hpp" -#include "util/Profiler.hpp" -#include "util/async/context/BasicExecutionContext.hpp" +#include "util/async/AnyExecutionContext.hpp" +#include "util/async/AnyOperation.hpp" #include "util/config/ConfigDefinition.hpp" #include "util/log/Logger.hpp" +#include #include +#include #include #include #include @@ -64,15 +69,12 @@ #include #include -#include +#include #include +#include #include #include -#include -#include #include -#include -#include namespace etlng { @@ -92,191 +94,94 @@ namespace etlng { class ETLService : public ETLServiceInterface { util::Logger log_{"ETL"}; + util::async::AnyExecutionContext ctx_; + std::reference_wrapper config_; std::shared_ptr backend_; - std::shared_ptr subscriptions_; - std::shared_ptr balancer_; + std::shared_ptr balancer_; std::shared_ptr ledgers_; - std::shared_ptr> cacheLoader_; - - std::shared_ptr fetcher_; + std::shared_ptr publisher_; + std::shared_ptr cacheLoader_; + std::shared_ptr cacheUpdater_; std::shared_ptr extractor_; + std::shared_ptr loader_; + std::shared_ptr initialLoadObserver_; + std::shared_ptr taskManagerProvider_; + std::shared_ptr state_; - etl::SystemState state_; - util::async::CoroExecutionContext ctx_{8}; + std::unique_ptr monitor_; + std::unique_ptr taskMan_; - std::shared_ptr amendmentBlockHandler_; - std::shared_ptr loader_; + boost::signals2::scoped_connection monitorSubscription_; - std::optional> mainLoop_; + std::optional> mainLoop_; public: /** * @brief Create an instance of ETLService. * - * @param config The configuration to use - * @param backend BackendInterface implementation - * @param subscriptions Subscription manager - * @param balancer Load balancer to use - * @param ledgers The network validated ledgers datastructure + * @param ctx The execution context for asynchronous operations + * @param config The Clio configuration definition + * @param backend Interface to the backend database + * @param balancer Load balancer for distributing work + * @param ledgers Interface for accessing network validated ledgers + * @param publisher Interface for publishing ledger data + * @param cacheLoader Interface for loading cache data + * @param cacheUpdater Interface for updating cache data + * @param extractor The extractor to use + * @param loader Interface for loading data + * @param initialLoadObserver The observer for initial data loading + * @param taskManagerProvider The provider of the task manager instance + * @param state System state tracking object */ ETLService( - util::config::ClioConfigDefinition const& config, - std::shared_ptr backend, - std::shared_ptr subscriptions, - std::shared_ptr balancer, - std::shared_ptr ledgers - ) - : backend_(std::move(backend)) - , subscriptions_(std::move(subscriptions)) - , balancer_(std::move(balancer)) - , ledgers_(std::move(ledgers)) - , cacheLoader_(std::make_shared>(config, backend_, backend_->cache())) - , fetcher_(std::make_shared(backend_, balancer_)) - , extractor_(std::make_shared(fetcher_)) - , amendmentBlockHandler_(std::make_shared(ctx_, state_)) - , loader_(std::make_shared( - backend_, - fetcher_, - impl::makeRegistry( - impl::CacheExt{backend_->cache()}, - impl::CoreExt{backend_}, - impl::SuccessorExt{backend_, backend_->cache()}, - impl::NFTExt{backend_} - ), - amendmentBlockHandler_ - )) - { - LOG(log_.info()) << "Creating ETLng..."; - } + util::async::AnyExecutionContext ctx, + std::reference_wrapper config, + std::shared_ptr backend, + std::shared_ptr balancer, + std::shared_ptr ledgers, + std::shared_ptr publisher, + std::shared_ptr cacheLoader, + std::shared_ptr cacheUpdater, + std::shared_ptr extractor, + std::shared_ptr loader, + std::shared_ptr initialLoadObserver, + std::shared_ptr taskManagerProvider, + std::shared_ptr state + ); - ~ETLService() override - { - LOG(log_.debug()) << "Stopping ETLng"; - } + ~ETLService() override; void - run() override - { - LOG(log_.info()) << "run() in ETLng..."; - - mainLoop_.emplace(ctx_.execute([this] { - auto const rng = loadInitialLedgerIfNeeded(); - - LOG(log_.info()) << "Waiting for next ledger to be validated by network..."; - std::optional const mostRecentValidated = ledgers_->getMostRecent(); - - if (not mostRecentValidated) { - LOG(log_.info()) << "The wait for the next validated ledger has been aborted. " - "Exiting monitor loop"; - return; - } - - ASSERT(rng.has_value(), "Ledger range can't be null"); - auto const nextSequence = rng->maxSequence + 1; - - LOG(log_.debug()) << "Database is populated. Starting monitor loop. sequence = " << nextSequence; - - auto scheduler = impl::makeScheduler(impl::ForwardScheduler{*ledgers_, nextSequence} - // impl::BackfillScheduler{nextSequence - 1, nextSequence - 1000}, - // TODO lift limit and start with rng.minSeq - ); - - auto man = impl::TaskManager(ctx_, *scheduler, *extractor_, *loader_); - - // TODO: figure out this: std::make_shared(backend_, ledgers_, nextSequence) - man.run({}); // TODO: needs to be interruptible and fill out settings - })); - } + run() override; void - stop() override - { - LOG(log_.info()) << "Stop called"; - // TODO: stop the service correctly - } + stop() override; boost::json::object - getInfo() const override - { - // TODO - return {{"ok", true}}; - } + getInfo() const override; bool - isAmendmentBlocked() const override - { - // TODO - return false; - } + isAmendmentBlocked() const override; bool - isCorruptionDetected() const override - { - // TODO - return false; - } + isCorruptionDetected() const override; std::optional - getETLState() const override - { - // TODO - return std::nullopt; - } + getETLState() const override; std::uint32_t - lastCloseAgeSeconds() const override - { - // TODO - return 0; - } + lastCloseAgeSeconds() const override; private: // TODO: this better be std::expected std::optional - loadInitialLedgerIfNeeded() - { - if (auto rng = backend_->hardFetchLedgerRangeNoThrow(); not rng.has_value()) { - LOG(log_.info()) << "Database is empty. Will download a ledger from the network."; + loadInitialLedgerIfNeeded(); - try { - LOG(log_.info()) << "Waiting for next ledger to be validated by network..."; - if (auto const mostRecentValidated = ledgers_->getMostRecent(); mostRecentValidated.has_value()) { - auto const seq = *mostRecentValidated; - LOG(log_.info()) << "Ledger " << seq << " has been validated. Downloading... "; + void + startMonitor(uint32_t seq); - auto [ledger, timeDiff] = ::util::timed>([this, seq]() { - return extractor_->extractLedgerOnly(seq).and_then([this, seq](auto&& data) { - // TODO: loadInitialLedger in balancer should be called fetchEdgeKeys or similar - data.edgeKeys = balancer_->loadInitialLedger(seq, *loader_); - - // TODO: this should be interruptible for graceful shutdown - return loader_->loadInitialLedger(data); - }); - }); - - LOG(log_.debug()) << "Time to download and store ledger = " << timeDiff; - LOG(log_.info()) << "Finished loadInitialLedger. cache size = " << backend_->cache().size(); - - if (ledger.has_value()) - return backend_->hardFetchLedgerRangeNoThrow(); - - LOG(log_.error()) << "Failed to load initial ledger. Exiting monitor loop"; - } else { - LOG(log_.info()) << "The wait for the next validated ledger has been aborted. " - "Exiting monitor loop"; - } - } catch (std::runtime_error const& e) { - LOG(log_.fatal()) << "Failed to load initial ledger: " << e.what(); - amendmentBlockHandler_->notifyAmendmentBlocked(); - } - } else { - LOG(log_.info()) << "Database already populated. Picking up from the tip of history"; - cacheLoader_->load(rng->maxSequence); - - return rng; - } - - return std::nullopt; - } + void + startLoading(uint32_t seq); }; + } // namespace etlng diff --git a/src/etlng/LedgerPublisherInterface.hpp b/src/etlng/LedgerPublisherInterface.hpp index fa6096251..29ec3fce7 100644 --- a/src/etlng/LedgerPublisherInterface.hpp +++ b/src/etlng/LedgerPublisherInterface.hpp @@ -37,13 +37,38 @@ struct LedgerPublisherInterface { * @param seq The sequence number of the ledger * @param maxAttempts The maximum number of attempts to publish the ledger; no limit if nullopt * @param attemptsDelay The delay between attempts + * @return Whether the ledger was found in the database and published */ - virtual void + virtual bool publish( uint32_t seq, std::optional maxAttempts, std::chrono::steady_clock::duration attemptsDelay = std::chrono::seconds{1} ) = 0; + + /** + * @brief Get last publish time as a time point + * + * @return A std::chrono::time_point representing the time of the last publish + */ + virtual std::chrono::time_point + getLastPublish() const = 0; + + /** + * @brief Get time passed since last ledger close, in seconds + * + * @return The number of seconds since the last ledger close as std::uint32_t + */ + virtual std::uint32_t + lastCloseAgeSeconds() const = 0; + + /** + * @brief Get time passed since last publish, in seconds + * + * @return The number of seconds since the last publish as std::uint32_t + */ + virtual std::uint32_t + lastPublishAgeSeconds() const = 0; }; } // namespace etlng diff --git a/src/etlng/MonitorInterface.hpp b/src/etlng/MonitorInterface.hpp index 8e4b745e9..dfae93447 100644 --- a/src/etlng/MonitorInterface.hpp +++ b/src/etlng/MonitorInterface.hpp @@ -40,6 +40,13 @@ public: virtual ~MonitorInterface() = default; + /** + * @brief Allows the loading process to notify of a freshly committed ledger + * @param seq The ledger sequence loaded + */ + virtual void + notifyLedgerLoaded(uint32_t seq) = 0; + /** * @brief Allows clients to get notified when a new ledger becomes available in Clio's database * diff --git a/src/etlng/TaskManagerInterface.hpp b/src/etlng/TaskManagerInterface.hpp new file mode 100644 index 000000000..95651ffa5 --- /dev/null +++ b/src/etlng/TaskManagerInterface.hpp @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include + +namespace etlng { + +/** + * @brief An interface for the Task Manager + */ +struct TaskManagerInterface { + virtual ~TaskManagerInterface() = default; + + /** + * @brief Start the task manager with specified settings + * @param numExtractors The number of extraction tasks + */ + virtual void + run(size_t numExtractors) = 0; + + /** + * @brief Stop the task manager + */ + virtual void + stop() = 0; +}; + +} // namespace etlng diff --git a/src/etlng/TaskManagerProviderInterface.hpp b/src/etlng/TaskManagerProviderInterface.hpp new file mode 100644 index 000000000..2894170bb --- /dev/null +++ b/src/etlng/TaskManagerProviderInterface.hpp @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include "etlng/MonitorInterface.hpp" +#include "etlng/TaskManagerInterface.hpp" +#include "util/async/AnyExecutionContext.hpp" + +#include +#include +#include +#include + +namespace etlng { + +/** + * @brief An interface for providing the Task Manager + */ +struct TaskManagerProviderInterface { + virtual ~TaskManagerProviderInterface() = default; + + /** + * @brief Make a task manager + * + * @param ctx The async context to associate the task manager instance with + * @param monitor The monitor to notify when ledger is loaded + * @param seq The sequence to start at + * @return A unique pointer to a TaskManager implementation + */ + virtual std::unique_ptr + make(util::async::AnyExecutionContext ctx, std::reference_wrapper monitor, uint32_t seq) = 0; +}; + +} // namespace etlng diff --git a/src/etlng/impl/AmendmentBlockHandler.cpp b/src/etlng/impl/AmendmentBlockHandler.cpp index 502e31812..efb9fbc4a 100644 --- a/src/etlng/impl/AmendmentBlockHandler.cpp +++ b/src/etlng/impl/AmendmentBlockHandler.cpp @@ -36,7 +36,7 @@ AmendmentBlockHandler::ActionType const AmendmentBlockHandler::kDEFAULT_AMENDMEN }; AmendmentBlockHandler::AmendmentBlockHandler( - util::async::AnyExecutionContext&& ctx, + util::async::AnyExecutionContext ctx, etl::SystemState& state, std::chrono::steady_clock::duration interval, ActionType action diff --git a/src/etlng/impl/AmendmentBlockHandler.hpp b/src/etlng/impl/AmendmentBlockHandler.hpp index 0bece8e79..6275d4ea0 100644 --- a/src/etlng/impl/AmendmentBlockHandler.hpp +++ b/src/etlng/impl/AmendmentBlockHandler.hpp @@ -50,7 +50,7 @@ public: static ActionType const kDEFAULT_AMENDMENT_BLOCK_ACTION; AmendmentBlockHandler( - util::async::AnyExecutionContext&& ctx, + util::async::AnyExecutionContext ctx, etl::SystemState& state, std::chrono::steady_clock::duration interval = std::chrono::seconds{1}, ActionType action = kDEFAULT_AMENDMENT_BLOCK_ACTION diff --git a/src/etlng/impl/CacheUpdater.hpp b/src/etlng/impl/CacheUpdater.hpp new file mode 100644 index 000000000..7e2646ef0 --- /dev/null +++ b/src/etlng/impl/CacheUpdater.hpp @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include "data/LedgerCacheInterface.hpp" +#include "data/Types.hpp" +#include "etlng/CacheUpdaterInterface.hpp" +#include "etlng/Models.hpp" +#include "util/log/Logger.hpp" + +#include +#include +#include + +namespace etlng::impl { + +class CacheUpdater : public CacheUpdaterInterface { + std::reference_wrapper cache_; + + util::Logger log_{"ETL"}; + +public: + CacheUpdater(data::LedgerCacheInterface& cache) : cache_{cache} + { + } + + void + update(model::LedgerData const& data) override + { + cache_.get().update(data.objects, data.seq); + } + + void + update(uint32_t seq, std::vector const& objs) override + { + cache_.get().update(objs, seq); + } + + void + update(uint32_t seq, std::vector const& objs) override + { + cache_.get().update(objs, seq); + } + + void + setFull() override + { + cache_.get().setFull(); + } +}; + +} // namespace etlng::impl diff --git a/src/etlng/impl/Extraction.hpp b/src/etlng/impl/Extraction.hpp index ea09642a2..9a2df8ba1 100644 --- a/src/etlng/impl/Extraction.hpp +++ b/src/etlng/impl/Extraction.hpp @@ -90,6 +90,13 @@ public: { } + Extractor(Extractor const&) = delete; + Extractor(Extractor&&) = delete; + Extractor& + operator=(Extractor const&) = delete; + Extractor& + operator=(Extractor&&) = delete; + [[nodiscard]] std::optional extractLedgerWithDiff(uint32_t seq) override; diff --git a/src/etlng/impl/LedgerPublisher.hpp b/src/etlng/impl/LedgerPublisher.hpp new file mode 100644 index 000000000..2c0d9ed7f --- /dev/null +++ b/src/etlng/impl/LedgerPublisher.hpp @@ -0,0 +1,286 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include "data/BackendInterface.hpp" +#include "data/DBHelpers.hpp" +#include "data/Types.hpp" +#include "etl/SystemState.hpp" +#include "etlng/LedgerPublisherInterface.hpp" +#include "etlng/impl/Loading.hpp" +#include "feed/SubscriptionManagerInterface.hpp" +#include "util/Assert.hpp" +#include "util/Mutex.hpp" +#include "util/log/Logger.hpp" +#include "util/prometheus/Counter.hpp" +#include "util/prometheus/Prometheus.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace etlng::impl { + +/** + * @brief Publishes ledgers in a synchronized fashion. + * + * If ETL is started far behind the network, ledgers will be written and published very rapidly. Monitoring processes + * will publish ledgers as they are written. However, to publish a ledger, the monitoring process needs to read all of + * the transactions for that ledger from the database. Reading the transactions from the database requires network + * calls, which can be slow. It is imperative however that the monitoring processes keep up with the writer, else the + * monitoring processes will not be able to detect if the writer failed. Therefore, publishing each ledger (which + * includes reading all of the transactions from the database) is done from the application wide asio io_service, and a + * strand is used to ensure ledgers are published in order. + */ +class LedgerPublisher : public etlng::LedgerPublisherInterface { + util::Logger log_{"ETL"}; + + boost::asio::strand publishStrand_; + + std::shared_ptr backend_; + std::shared_ptr subscriptions_; + std::reference_wrapper state_; // shared state for ETL + + util::Mutex, std::shared_mutex> lastCloseTime_; + + std::reference_wrapper lastPublishSeconds_ = PrometheusService::counterInt( + "etl_last_publish_seconds", + {}, + "Seconds since epoch of the last published ledger" + ); + + util::Mutex, std::shared_mutex> lastPublishedSequence_; + +public: + /** + * @brief Create an instance of the publisher + */ + LedgerPublisher( + boost::asio::io_context& ioc, // TODO: replace with AsyncContext shared with ETLServiceNg + std::shared_ptr backend, + std::shared_ptr subscriptions, + etl::SystemState const& state + ) + : publishStrand_{boost::asio::make_strand(ioc)} + , backend_{std::move(backend)} + , subscriptions_{std::move(subscriptions)} + , state_{std::cref(state)} + { + } + + /** + * @brief Attempt to read the specified ledger from the database, and then publish that ledger to the ledgers + * stream. + * + * @param ledgerSequence the sequence of the ledger to publish + * @param maxAttempts the number of times to attempt to read the ledger from the database + * @param attemptsDelay the delay between attempts to read the ledger from the database + * @return Whether the ledger was found in the database and published + */ + bool + publish( + uint32_t ledgerSequence, + std::optional maxAttempts, + std::chrono::steady_clock::duration attemptsDelay = std::chrono::seconds{1} + ) override + { + LOG(log_.info()) << "Attempting to publish ledger = " << ledgerSequence; + size_t numAttempts = 0; + while (not state_.get().isStopping) { + auto range = backend_->hardFetchLedgerRangeNoThrow(); + + if (!range || range->maxSequence < ledgerSequence) { + ++numAttempts; + LOG(log_.debug()) << "Trying to publish. Could not find ledger with sequence = " << ledgerSequence; + + // We try maxAttempts times to publish the ledger, waiting one second in between each attempt. + if (maxAttempts && numAttempts >= maxAttempts) { + LOG(log_.debug()) << "Failed to publish ledger after " << numAttempts << " attempts."; + return false; + } + std::this_thread::sleep_for(attemptsDelay); + continue; + } + + auto lgr = data::synchronousAndRetryOnTimeout([&](auto yield) { + return backend_->fetchLedgerBySequence(ledgerSequence, yield); + }); + + ASSERT(lgr.has_value(), "Ledger must exist in database. Ledger sequence = {}", ledgerSequence); + publish(*lgr); + + return true; + } + return false; + } + + /** + * @brief Publish the passed ledger asynchronously. + * + * All ledgers are published thru publishStrand_ which ensures that all publishes are performed in a serial fashion. + * + * @param lgrInfo the ledger to publish + */ + void + publish(ripple::LedgerHeader const& lgrInfo) + { + boost::asio::post(publishStrand_, [this, lgrInfo = lgrInfo]() { + LOG(log_.info()) << "Publishing ledger " << std::to_string(lgrInfo.seq); + + // TODO: This should probably not be part of publisher in the future + if (not state_.get().isWriting) + backend_->updateRange(lgrInfo.seq); // This can't be unit tested atm. + + setLastClose(lgrInfo.closeTime); + auto age = lastCloseAgeSeconds(); + + // if the ledger closed over MAX_LEDGER_AGE_SECONDS ago, assume we are still catching up and don't publish + static constexpr std::uint32_t kMAX_LEDGER_AGE_SECONDS = 600; + if (age < kMAX_LEDGER_AGE_SECONDS) { + std::optional fees = data::synchronousAndRetryOnTimeout([&](auto yield) { + return backend_->fetchFees(lgrInfo.seq, yield); + }); + ASSERT(fees.has_value(), "Fees must exist for ledger {}", lgrInfo.seq); + + auto transactions = data::synchronousAndRetryOnTimeout([&](auto yield) { + return backend_->fetchAllTransactionsInLedger(lgrInfo.seq, yield); + }); + + auto const ledgerRange = backend_->fetchLedgerRange(); + ASSERT(ledgerRange.has_value(), "Ledger range must exist"); + + auto const range = fmt::format("{}-{}", ledgerRange->minSequence, ledgerRange->maxSequence); + subscriptions_->pubLedger(lgrInfo, *fees, range, transactions.size()); + + // order with transaction index + std::ranges::sort(transactions, [](auto const& t1, auto const& t2) { + ripple::SerialIter iter1{t1.metadata.data(), t1.metadata.size()}; + ripple::STObject const object1(iter1, ripple::sfMetadata); + ripple::SerialIter iter2{t2.metadata.data(), t2.metadata.size()}; + ripple::STObject const object2(iter2, ripple::sfMetadata); + return object1.getFieldU32(ripple::sfTransactionIndex) < + object2.getFieldU32(ripple::sfTransactionIndex); + }); + + for (auto const& txAndMeta : transactions) + subscriptions_->pubTransaction(txAndMeta, lgrInfo); + + subscriptions_->pubBookChanges(lgrInfo, transactions); + + setLastPublishTime(); + LOG(log_.info()) << "Published ledger " << lgrInfo.seq; + } else { + LOG(log_.info()) << "Skipping publishing ledger " << lgrInfo.seq; + } + }); + + // we track latest publish-requested seq, not necessarily already published + setLastPublishedSequence(lgrInfo.seq); + } + + /** + * @brief Get time passed since last publish, in seconds + */ + std::uint32_t + lastPublishAgeSeconds() const override + { + return std::chrono::duration_cast(std::chrono::system_clock::now() - getLastPublish()) + .count(); + } + + /** + * @brief Get last publish time as a time point + */ + std::chrono::time_point + getLastPublish() const override + { + return std::chrono::time_point{std::chrono::seconds{lastPublishSeconds_.get().value() + }}; + } + + /** + * @brief Get time passed since last ledger close, in seconds + */ + std::uint32_t + lastCloseAgeSeconds() const override + { + auto closeTime = lastCloseTime_.lock()->time_since_epoch().count(); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + if (now < (kRIPPLE_EPOCH_START + closeTime)) + return 0; + return now - (kRIPPLE_EPOCH_START + closeTime); + } + + /** + * @brief Get the sequence of the last schueduled ledger to publish, Be aware that the ledger may not have been + * published to network + */ + std::optional + getLastPublishedSequence() const + { + return *lastPublishedSequence_.lock(); + } + +private: + void + setLastClose(std::chrono::time_point lastCloseTime) + { + auto closeTime = lastCloseTime_.lock(); + *closeTime = lastCloseTime; + } + + void + setLastPublishTime() + { + using namespace std::chrono; + auto const nowSeconds = duration_cast(system_clock::now().time_since_epoch()).count(); + lastPublishSeconds_.get().set(nowSeconds); + } + + void + setLastPublishedSequence(std::optional lastPublishedSequence) + { + auto lastPublishSeq = lastPublishedSequence_.lock(); + *lastPublishSeq = lastPublishedSequence; + } +}; + +} // namespace etlng::impl diff --git a/src/etlng/impl/Loading.cpp b/src/etlng/impl/Loading.cpp index e8608dbed..701fe3597 100644 --- a/src/etlng/impl/Loading.cpp +++ b/src/etlng/impl/Loading.cpp @@ -20,7 +20,6 @@ #include "etlng/impl/Loading.hpp" #include "data/BackendInterface.hpp" -#include "etl/LedgerFetcherInterface.hpp" #include "etl/impl/LedgerLoader.hpp" #include "etlng/AmendmentBlockHandlerInterface.hpp" #include "etlng/Models.hpp" @@ -46,12 +45,10 @@ namespace etlng::impl { Loader::Loader( std::shared_ptr backend, - std::shared_ptr fetcher, std::shared_ptr registry, std::shared_ptr amendmentBlockHandler ) : backend_(std::move(backend)) - , fetcher_(std::move(fetcher)) , registry_(std::move(registry)) , amendmentBlockHandler_(std::move(amendmentBlockHandler)) { @@ -81,31 +78,44 @@ Loader::onInitialLoadGotMoreObjects( std::optional lastKey ) { - LOG(log_.debug()) << "On initial load: got more objects for seq " << seq << ". size = " << data.size(); - registry_->dispatchInitialObjects( - seq, data, std::move(lastKey).value_or(std::string{}) // TODO: perhaps use optional all the way to extensions? - ); + try { + LOG(log_.debug()) << "On initial load: got more objects for seq " << seq << ". size = " << data.size(); + registry_->dispatchInitialObjects( + seq, + data, + std::move(lastKey).value_or(std::string{}) // TODO: perhaps use optional all the way to extensions? + ); + } catch (std::runtime_error const& e) { + LOG(log_.fatal()) << "Failed to load initial objects for " << seq << ": " << e.what(); + amendmentBlockHandler_->notifyAmendmentBlocked(); + } } std::optional Loader::loadInitialLedger(model::LedgerData const& data) { - // check that database is actually empty - auto rng = backend_->hardFetchLedgerRangeNoThrow(); - if (rng) { - ASSERT(false, "Database is not empty"); + try { + // check that database is actually empty + auto rng = backend_->hardFetchLedgerRangeNoThrow(); + if (rng) { + ASSERT(false, "Database is not empty"); + return std::nullopt; + } + + LOG(log_.debug()) << "Deserialized ledger header. " << ::util::toString(data.header); + + auto seconds = ::util::timed([this, &data]() { registry_->dispatchInitialData(data); }); + LOG(log_.info()) << "Dispatching initial data and submitting all writes took " << seconds << " seconds."; + + backend_->finishWrites(data.seq); + LOG(log_.debug()) << "Loaded initial ledger"; + + return {data.header}; + } catch (std::runtime_error const& e) { + LOG(log_.fatal()) << "Failed to load initial ledger " << data.seq << ": " << e.what(); + amendmentBlockHandler_->notifyAmendmentBlocked(); return std::nullopt; } - - LOG(log_.debug()) << "Deserialized ledger header. " << ::util::toString(data.header); - - auto seconds = ::util::timed([this, &data]() { registry_->dispatchInitialData(data); }); - LOG(log_.info()) << "Dispatching initial data and submitting all writes took " << seconds << " seconds."; - - backend_->finishWrites(data.seq); - LOG(log_.debug()) << "Loaded initial ledger"; - - return {data.header}; } } // namespace etlng::impl diff --git a/src/etlng/impl/Loading.hpp b/src/etlng/impl/Loading.hpp index caa677bce..39a1e150a 100644 --- a/src/etlng/impl/Loading.hpp +++ b/src/etlng/impl/Loading.hpp @@ -49,7 +49,6 @@ namespace etlng::impl { class Loader : public LoaderInterface, public InitialLoadObserverInterface { std::shared_ptr backend_; - std::shared_ptr fetcher_; std::shared_ptr registry_; std::shared_ptr amendmentBlockHandler_; @@ -62,11 +61,17 @@ public: Loader( std::shared_ptr backend, - std::shared_ptr fetcher, std::shared_ptr registry, std::shared_ptr amendmentBlockHandler ); + Loader(Loader const&) = delete; + Loader(Loader&&) = delete; + Loader& + operator=(Loader const&) = delete; + Loader& + operator=(Loader&&) = delete; + void load(model::LedgerData const& data) override; diff --git a/src/etlng/impl/Monitor.cpp b/src/etlng/impl/Monitor.cpp index 8b8bedad2..e55eb3456 100644 --- a/src/etlng/impl/Monitor.cpp +++ b/src/etlng/impl/Monitor.cpp @@ -55,6 +55,15 @@ Monitor::~Monitor() stop(); } +// TODO: think about using signals perhaps? maybe combining with onNextSequence? +// also, how do we not double invoke or does it not matter +void +Monitor::notifyLedgerLoaded(uint32_t seq) +{ + LOG(log_.debug()) << "Loader notified about newly committed ledger " << seq; + repeatedTask_->invoke(); // force-invoke immediately +}; + void Monitor::run(std::chrono::steady_clock::duration repeatInterval) { diff --git a/src/etlng/impl/Monitor.hpp b/src/etlng/impl/Monitor.hpp index 9b5112cc3..b8971bc84 100644 --- a/src/etlng/impl/Monitor.hpp +++ b/src/etlng/impl/Monitor.hpp @@ -60,6 +60,9 @@ public: ); ~Monitor() override; + void + notifyLedgerLoaded(uint32_t seq) override; + void run(std::chrono::steady_clock::duration repeatInterval) override; diff --git a/src/etlng/impl/Registry.hpp b/src/etlng/impl/Registry.hpp index 921b70811..c1a43ae8f 100644 --- a/src/etlng/impl/Registry.hpp +++ b/src/etlng/impl/Registry.hpp @@ -19,12 +19,15 @@ #pragma once +#include "etl/SystemState.hpp" #include "etlng/Models.hpp" #include "etlng/RegistryInterface.hpp" #include +#include #include +#include #include #include #include @@ -88,6 +91,7 @@ concept SomeExtension = NoTwoOfKind and ContainsValidHook; template class Registry : public RegistryInterface { + std::reference_wrapper state_; std::tuple store_; static_assert( @@ -101,9 +105,9 @@ class Registry : public RegistryInterface { ); public: - explicit constexpr Registry(SomeExtension auto&&... exts) + explicit constexpr Registry(etl::SystemState const& state, SomeExtension auto&&... exts) requires(std::is_same_v, std::decay_t> and ...) - : store_(std::forward(exts)...) + : state_{state}, store_(std::forward(exts)...) { } @@ -121,9 +125,8 @@ public: // send entire batch of data at once { auto const expand = [&](auto& p) { - if constexpr (requires { p.onLedgerData(data); }) { - p.onLedgerData(data); - } + if constexpr (requires { p.onLedgerData(data); }) + executeIfAllowed(p, [&data](auto& p) { p.onLedgerData(data); }); }; std::apply([&expand](auto&&... xs) { (expand(xs), ...); }, store_); @@ -134,7 +137,7 @@ public: auto const expand = [&](P& p, model::Transaction const& t) { if constexpr (requires { p.onTransaction(data.seq, t); }) { if (std::decay_t

::spec::wants(t.type)) - p.onTransaction(data.seq, t); + executeIfAllowed(p, [&data, &t](auto& p) { p.onTransaction(data.seq, t); }); } }; @@ -146,9 +149,8 @@ public: // send per object path { auto const expand = [&](P&& p, model::Object const& o) { - if constexpr (requires { p.onObject(data.seq, o); }) { - p.onObject(data.seq, o); - } + if constexpr (requires { p.onObject(data.seq, o); }) + executeIfAllowed(p, [&data, &o](auto& p) { p.onObject(data.seq, o); }); }; for (auto const& obj : data.objects) { @@ -163,9 +165,8 @@ public: // send entire vector path { auto const expand = [&](auto&& p) { - if constexpr (requires { p.onInitialObjects(seq, data, lastKey); }) { - p.onInitialObjects(seq, data, lastKey); - } + if constexpr (requires { p.onInitialObjects(seq, data, lastKey); }) + executeIfAllowed(p, [seq, &data, &lastKey](auto& p) { p.onInitialObjects(seq, data, lastKey); }); }; std::apply([&expand](auto&&... xs) { (expand(xs), ...); }, store_); @@ -174,9 +175,8 @@ public: // send per object path { auto const expand = [&](P&& p, model::Object const& o) { - if constexpr (requires { p.onInitialObject(seq, o); }) { - p.onInitialObject(seq, o); - } + if constexpr (requires { p.onInitialObject(seq, o); }) + executeIfAllowed(p, [seq, &o](auto& p) { p.onInitialObject(seq, o); }); }; for (auto const& obj : data) { @@ -191,9 +191,8 @@ public: // send entire batch path { auto const expand = [&](auto&& p) { - if constexpr (requires { p.onInitialData(data); }) { - p.onInitialData(data); - } + if constexpr (requires { p.onInitialData(data); }) + executeIfAllowed(p, [&data](auto& p) { p.onInitialData(data); }); }; std::apply([&expand](auto&&... xs) { (expand(xs), ...); }, store_); @@ -204,7 +203,7 @@ public: auto const expand = [&](P&& p, model::Transaction const& tx) { if constexpr (requires { p.onInitialTransaction(data.seq, tx); }) { if (std::decay_t

::spec::wants(tx.type)) - p.onInitialTransaction(data.seq, tx); + executeIfAllowed(p, [&data, &tx](auto& p) { p.onInitialTransaction(data.seq, tx); }); } }; @@ -213,12 +212,25 @@ public: } } } + +private: + void + executeIfAllowed(auto& p, auto&& fn) + { + if constexpr (requires { p.allowInReadonly(); }) { + if (state_.get().isWriting or p.allowInReadonly()) + fn(p); + } else { + if (state_.get().isWriting) + fn(p); + } + } }; static auto -makeRegistry(auto&&... exts) +makeRegistry(etl::SystemState const& state, auto&&... exts) { - return std::make_unique...>>(std::forward(exts)...); + return std::make_unique...>>(state, std::forward(exts)...); } } // namespace etlng::impl diff --git a/src/etlng/impl/TaskManager.cpp b/src/etlng/impl/TaskManager.cpp index a5fc20ce0..8f61dc445 100644 --- a/src/etlng/impl/TaskManager.cpp +++ b/src/etlng/impl/TaskManager.cpp @@ -22,15 +22,21 @@ #include "etlng/ExtractorInterface.hpp" #include "etlng/LoaderInterface.hpp" #include "etlng/Models.hpp" +#include "etlng/MonitorInterface.hpp" #include "etlng/SchedulerInterface.hpp" +#include "etlng/impl/Monitor.hpp" +#include "etlng/impl/TaskQueue.hpp" +#include "util/LedgerUtils.hpp" +#include "util/Profiler.hpp" #include "util/async/AnyExecutionContext.hpp" #include "util/async/AnyOperation.hpp" -#include "util/async/AnyStrand.hpp" #include "util/log/Logger.hpp" #include #include +#include #include +#include #include #include #include @@ -39,12 +45,19 @@ namespace etlng::impl { TaskManager::TaskManager( - util::async::AnyExecutionContext&& ctx, - std::reference_wrapper scheduler, + util::async::AnyExecutionContext ctx, + std::shared_ptr scheduler, std::reference_wrapper extractor, - std::reference_wrapper loader + std::reference_wrapper loader, + std::reference_wrapper monitor, + uint32_t startSeq ) - : ctx_(std::move(ctx)), schedulers_(scheduler), extractor_(extractor), loader_(loader) + : ctx_(std::move(ctx)) + , schedulers_(std::move(scheduler)) + , extractor_(extractor) + , loader_(loader) + , monitor_(monitor) + , queue_({.startSeq = startSeq, .increment = 1u, .limit = kQUEUE_SIZE_LIMIT}) { } @@ -54,37 +67,32 @@ TaskManager::~TaskManager() } void -TaskManager::run(Settings settings) +TaskManager::run(std::size_t numExtractors) { - static constexpr auto kQUEUE_SIZE_LIMIT = 2048uz; + LOG(log_.debug()) << "Starting task manager with " << numExtractors << " extractors...\n"; - auto schedulingStrand = ctx_.makeStrand(); - PriorityQueue queue(ctx_.makeStrand(), kQUEUE_SIZE_LIMIT); + stop(); + extractors_.clear(); + loaders_.clear(); - LOG(log_.debug()) << "Starting task manager...\n"; + extractors_.reserve(numExtractors); + for ([[maybe_unused]] auto _ : std::views::iota(0uz, numExtractors)) + extractors_.push_back(spawnExtractor(queue_)); - extractors_.reserve(settings.numExtractors); - for ([[maybe_unused]] auto _ : std::views::iota(0uz, settings.numExtractors)) - extractors_.push_back(spawnExtractor(schedulingStrand, queue)); - - loaders_.reserve(settings.numLoaders); - for ([[maybe_unused]] auto _ : std::views::iota(0uz, settings.numLoaders)) - loaders_.push_back(spawnLoader(queue)); - - wait(); - LOG(log_.debug()) << "All finished in task manager..\n"; + // Only one forward loader for now. Backfill to be added here later + loaders_.push_back(spawnLoader(queue_)); } util::async::AnyOperation -TaskManager::spawnExtractor(util::async::AnyStrand& strand, PriorityQueue& queue) +TaskManager::spawnExtractor(TaskQueue& queue) { // TODO: these values may be extracted to config later and/or need to be fine-tuned on a realistic system static constexpr auto kDELAY_BETWEEN_ATTEMPTS = std::chrono::milliseconds{100u}; static constexpr auto kDELAY_BETWEEN_ENQUEUE_ATTEMPTS = std::chrono::milliseconds{1u}; - return strand.execute([this, &queue](auto stopRequested) { + return ctx_.execute([this, &queue](auto stopRequested) { while (not stopRequested) { - if (auto task = schedulers_.get().next(); task.has_value()) { + if (auto task = schedulers_->next(); task.has_value()) { if (auto maybeBatch = extractor_.get().extractLedgerWithDiff(task->seq); maybeBatch.has_value()) { LOG(log_.debug()) << "Adding data after extracting diff"; while (not queue.enqueue(*maybeBatch)) { @@ -107,13 +115,26 @@ TaskManager::spawnExtractor(util::async::AnyStrand& strand, PriorityQueue& queue } util::async::AnyOperation -TaskManager::spawnLoader(PriorityQueue& queue) +TaskManager::spawnLoader(TaskQueue& queue) { + static constexpr auto kNANO_TO_SECOND = 1.0e9; + return ctx_.execute([this, &queue](auto stopRequested) { while (not stopRequested) { // TODO (https://github.com/XRPLF/clio/issues/66): does not tell the loader whether it's out of order or not - if (auto data = queue.dequeue(); data.has_value()) - loader_.get().load(*data); + if (auto data = queue.dequeue(); data.has_value()) { + auto nanos = util::timed([this, data = *data] { loader_.get().load(data); }); + auto const seconds = nanos / kNANO_TO_SECOND; + auto const txnCount = data->transactions.size(); + auto const objCount = data->objects.size(); + + LOG(log_.info()) << "Wrote ledger " << data->seq << " with header: " << util::toString(data->header) + << ". txns[" << txnCount << "]; objs[" << objCount << "]; in " << seconds + << " seconds;" + << " tps[" << txnCount / seconds << "], ops[" << objCount / seconds << "]"; + + monitor_.get().notifyLedgerLoaded(data->seq); + } } }); } diff --git a/src/etlng/impl/TaskManager.hpp b/src/etlng/impl/TaskManager.hpp index 0047513bc..48ab3f87b 100644 --- a/src/etlng/impl/TaskManager.hpp +++ b/src/etlng/impl/TaskManager.hpp @@ -21,74 +21,70 @@ #include "etlng/ExtractorInterface.hpp" #include "etlng/LoaderInterface.hpp" -#include "etlng/Models.hpp" +#include "etlng/MonitorInterface.hpp" #include "etlng/SchedulerInterface.hpp" -#include "util/StrandedPriorityQueue.hpp" +#include "etlng/TaskManagerInterface.hpp" +#include "etlng/impl/Monitor.hpp" +#include "etlng/impl/TaskQueue.hpp" #include "util/async/AnyExecutionContext.hpp" #include "util/async/AnyOperation.hpp" -#include "util/async/AnyStrand.hpp" #include "util/log/Logger.hpp" #include +#include #include +#include #include +#include #include namespace etlng::impl { -class TaskManager { +class TaskManager : public TaskManagerInterface { + static constexpr auto kQUEUE_SIZE_LIMIT = 2048uz; + util::async::AnyExecutionContext ctx_; - std::reference_wrapper schedulers_; + std::shared_ptr schedulers_; std::reference_wrapper extractor_; std::reference_wrapper loader_; + std::reference_wrapper monitor_; + + impl::TaskQueue queue_; + std::atomic_uint32_t nextForwardSequence_; std::vector> extractors_; std::vector> loaders_; util::Logger log_{"ETL"}; - struct ReverseOrderComparator { - [[nodiscard]] bool - operator()(model::LedgerData const& lhs, model::LedgerData const& rhs) const noexcept - { - return lhs.seq > rhs.seq; - } - }; - public: - struct Settings { - size_t numExtractors; /**< number of extraction tasks */ - size_t numLoaders; /**< number of loading tasks */ - }; - - // reverse order loading is needed (i.e. start with oldest seq in forward fill buffer) - using PriorityQueue = util::StrandedPriorityQueue; - TaskManager( - util::async::AnyExecutionContext&& ctx, - std::reference_wrapper scheduler, + util::async::AnyExecutionContext ctx, + std::shared_ptr scheduler, std::reference_wrapper extractor, - std::reference_wrapper loader + std::reference_wrapper loader, + std::reference_wrapper monitor, + uint32_t startSeq ); - ~TaskManager(); + ~TaskManager() override; void - run(Settings settings); + run(std::size_t numExtractors) override; void - stop(); + stop() override; private: void wait(); [[nodiscard]] util::async::AnyOperation - spawnExtractor(util::async::AnyStrand& strand, PriorityQueue& queue); + spawnExtractor(TaskQueue& queue); [[nodiscard]] util::async::AnyOperation - spawnLoader(PriorityQueue& queue); + spawnLoader(TaskQueue& queue); }; } // namespace etlng::impl diff --git a/src/etlng/impl/TaskManagerProvider.hpp b/src/etlng/impl/TaskManagerProvider.hpp new file mode 100644 index 000000000..f242d7ce8 --- /dev/null +++ b/src/etlng/impl/TaskManagerProvider.hpp @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include "etl/NetworkValidatedLedgersInterface.hpp" +#include "etlng/ExtractorInterface.hpp" +#include "etlng/LoaderInterface.hpp" +#include "etlng/MonitorInterface.hpp" +#include "etlng/TaskManagerInterface.hpp" +#include "etlng/TaskManagerProviderInterface.hpp" +#include "etlng/impl/Scheduling.hpp" +#include "etlng/impl/TaskManager.hpp" +#include "util/async/AnyExecutionContext.hpp" + +#include +#include +#include +#include + +namespace etlng::impl { + +/** + * @brief Implementation of the TaskManagerProvider interface + */ +class TaskManagerProvider : public TaskManagerProviderInterface { + std::reference_wrapper ledgers_; + std::shared_ptr extractor_; + std::shared_ptr loader_; + +public: + /** + * @brief Constructor + * + * @param ledgers Reference to ledgers + * @param extractor The extractor + * @param loader The loader + */ + TaskManagerProvider( + std::reference_wrapper ledgers, + std::shared_ptr extractor, + std::shared_ptr loader + ) + : ledgers_(ledgers), extractor_(std::move(extractor)), loader_(std::move(loader)) + { + } + + std::unique_ptr + make(util::async::AnyExecutionContext ctx, std::reference_wrapper monitor, uint32_t seq) override + { + auto scheduler = impl::makeScheduler(impl::ForwardScheduler{ledgers_, seq}); + // TODO: add impl::BackfillScheduler{seq - 1, seq - 1000}, + + return std::make_unique(std::move(ctx), std::move(scheduler), *extractor_, *loader_, monitor, seq); + } +}; + +} // namespace etlng::impl diff --git a/src/util/StrandedPriorityQueue.hpp b/src/etlng/impl/TaskQueue.hpp similarity index 54% rename from src/util/StrandedPriorityQueue.hpp rename to src/etlng/impl/TaskQueue.hpp index cd605e58f..40c7ac758 100644 --- a/src/util/StrandedPriorityQueue.hpp +++ b/src/etlng/impl/TaskQueue.hpp @@ -19,36 +19,58 @@ #pragma once -#include "util/async/AnyStrand.hpp" +#include "etlng/Models.hpp" +#include "util/Mutex.hpp" #include -#include +#include #include #include -#include #include #include -namespace util { +namespace etlng::impl { + +struct ReverseOrderComparator { + [[nodiscard]] bool + operator()(model::LedgerData const& lhs, model::LedgerData const& rhs) const noexcept + { + return lhs.seq > rhs.seq; + } +}; /** - * @brief A wrapper for std::priority_queue that serialises operations using a strand + * @brief A wrapper for std::priority_queue that serialises operations using a mutex * @note This may be a candidate for future improvements if performance proves to be poor (e.g. use a lock free queue) */ -template > -class StrandedPriorityQueue { - util::async::AnyStrand strand_; +class TaskQueue { std::size_t limit_; - std::priority_queue, Compare> queue_; + std::uint32_t increment_; + + struct Data { + std::uint32_t expectedSequence; + std::priority_queue, ReverseOrderComparator> forwardLoadQueue; + + Data(std::uint32_t seq) : expectedSequence(seq) + { + } + }; + + util::Mutex data_; public: + struct Settings { + std::uint32_t startSeq = 0u; // sequence to start from (for dequeue) + std::uint32_t increment = 1u; // increment sequence by this value once dequeue was successful + std::optional limit = std::nullopt; + }; + /** - * @brief Construct a new priority queue on a strand - * @param strand The strand to use + * @brief Construct a new priority queue * @param limit The limit of items allowed simultaneously in the queue */ - StrandedPriorityQueue(util::async::AnyStrand&& strand, std::optional limit = std::nullopt) - : strand_(std::move(strand)), limit_(limit.value_or(0uz)) + explicit TaskQueue(Settings settings) + : limit_(settings.limit.value_or(0uz)), increment_(settings.increment), data_(settings.startSeq) { } @@ -56,25 +78,20 @@ public: * @brief Enqueue a new item onto the queue if space is available * @note This function blocks until the item is attempted to be added to the queue * - * @tparam I Type of the item to add * @param item The item to add * @return true if item added to the queue; false otherwise */ - template [[nodiscard]] bool - enqueue(I&& item) - requires std::is_same_v, T> + enqueue(model::LedgerData item) { - return strand_ - .execute([&item, this] { - if (limit_ == 0uz or queue_.size() < limit_) { - queue_.push(std::forward(item)); - return true; - } - return false; - }) - .get() - .value_or(false); // if some exception happens - failed to add + auto lock = data_.lock(); + + if (limit_ == 0uz or lock->forwardLoadQueue.size() < limit_) { + lock->forwardLoadQueue.push(std::move(item)); + return true; + } + + return false; } /** @@ -82,22 +99,19 @@ public: * @note This function blocks until the item is taken off the queue * @return An item if available; nullopt otherwise */ - [[nodiscard]] std::optional + [[nodiscard]] std::optional dequeue() { - return strand_ - .execute([this] -> std::optional { - std::optional out; + auto lock = data_.lock(); + std::optional out; - if (not queue_.empty()) { - out.emplace(queue_.top()); - queue_.pop(); - } + if (not lock->forwardLoadQueue.empty() && lock->forwardLoadQueue.top().seq == lock->expectedSequence) { + out.emplace(lock->forwardLoadQueue.top()); + lock->forwardLoadQueue.pop(); + lock->expectedSequence += increment_; + } - return out; - }) - .get() - .value_or(std::nullopt); + return out; } /** @@ -109,8 +123,8 @@ public: [[nodiscard]] bool empty() { - return strand_.execute([this] { return queue_.empty(); }).get().value(); + return data_.lock()->forwardLoadQueue.empty(); } }; -} // namespace util +} // namespace etlng::impl diff --git a/src/etlng/impl/ext/Cache.cpp b/src/etlng/impl/ext/Cache.cpp index d7c2e5f51..221f639ac 100644 --- a/src/etlng/impl/ext/Cache.cpp +++ b/src/etlng/impl/ext/Cache.cpp @@ -19,24 +19,26 @@ #include "etlng/impl/ext/Cache.hpp" -#include "data/LedgerCacheInterface.hpp" +#include "etlng/CacheUpdaterInterface.hpp" #include "etlng/Models.hpp" #include "util/log/Logger.hpp" #include +#include #include +#include #include namespace etlng::impl { -CacheExt::CacheExt(data::LedgerCacheInterface& cache) : cache_(cache) +CacheExt::CacheExt(std::shared_ptr cacheUpdater) : cacheUpdater_(std::move(cacheUpdater)) { } void CacheExt::onLedgerData(model::LedgerData const& data) const { - cache_.get().update(data.objects, data.seq); + cacheUpdater_->update(data); LOG(log_.trace()) << "got data. objects cnt = " << data.objects.size(); } @@ -44,8 +46,8 @@ void CacheExt::onInitialData(model::LedgerData const& data) const { LOG(log_.trace()) << "got initial data. objects cnt = " << data.objects.size(); - cache_.get().update(data.objects, data.seq); - cache_.get().setFull(); + cacheUpdater_->update(data); + cacheUpdater_->setFull(); } void @@ -53,7 +55,7 @@ CacheExt::onInitialObjects(uint32_t seq, std::vector const& objs, const { LOG(log_.trace()) << "got initial objects cnt = " << objs.size(); - cache_.get().update(objs, seq); + cacheUpdater_->update(seq, objs); } } // namespace etlng::impl diff --git a/src/etlng/impl/ext/Cache.hpp b/src/etlng/impl/ext/Cache.hpp index fc9fbbeb4..51bf5cd56 100644 --- a/src/etlng/impl/ext/Cache.hpp +++ b/src/etlng/impl/ext/Cache.hpp @@ -19,24 +19,25 @@ #pragma once -#include "data/LedgerCacheInterface.hpp" +#include "etlng/CacheUpdaterInterface.hpp" #include "etlng/Models.hpp" +#include "etlng/impl/CacheUpdater.hpp" #include "util/log/Logger.hpp" #include -#include +#include #include #include namespace etlng::impl { class CacheExt { - std::reference_wrapper cache_; + std::shared_ptr cacheUpdater_; util::Logger log_{"ETL"}; public: - CacheExt(data::LedgerCacheInterface& cache); + CacheExt(std::shared_ptr cacheUpdater); void onLedgerData(model::LedgerData const& data) const; @@ -46,6 +47,13 @@ public: void onInitialObjects(uint32_t seq, std::vector const& objs, [[maybe_unused]] std::string lastKey) const; + + // We want cache updates through ETL if we are a potential writer but currently are not writing to DB + static bool + allowInReadonly() + { + return true; + } }; } // namespace etlng::impl diff --git a/tests/common/util/MockLedgerPublisher.hpp b/tests/common/util/MockLedgerPublisher.hpp index 574fc967a..26a24e7fb 100644 --- a/tests/common/util/MockLedgerPublisher.hpp +++ b/tests/common/util/MockLedgerPublisher.hpp @@ -19,6 +19,8 @@ #pragma once +#include "etlng/LedgerPublisherInterface.hpp" + #include #include @@ -26,11 +28,11 @@ #include #include -struct MockLedgerPublisher { - MOCK_METHOD(bool, publish, (uint32_t, std::optional), ()); +struct MockLedgerPublisher : public etlng::LedgerPublisherInterface { + MOCK_METHOD(bool, publish, (uint32_t, std::optional, std::chrono::steady_clock::duration), (override)); MOCK_METHOD(void, publish, (ripple::LedgerHeader const&), ()); MOCK_METHOD(std::uint32_t, lastPublishAgeSeconds, (), (const)); - MOCK_METHOD(std::chrono::time_point, getLastPublish, (), (const)); - MOCK_METHOD(std::uint32_t, lastCloseAgeSeconds, (), (const)); + MOCK_METHOD(std::chrono::time_point, getLastPublish, (), (const, override)); + MOCK_METHOD(std::uint32_t, lastCloseAgeSeconds, (), (const, override)); MOCK_METHOD(std::optional, getLastPublishedSequence, (), (const)); }; diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index b24429a90..33b2ffc01 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -40,9 +40,11 @@ target_sources( etl/TransformerTests.cpp # ETLng etlng/AmendmentBlockHandlerTests.cpp + etlng/ETLServiceTests.cpp etlng/ExtractionTests.cpp etlng/ForwardingSourceTests.cpp etlng/GrpcSourceTests.cpp + etlng/LedgerPublisherTests.cpp etlng/RegistryTests.cpp etlng/SchedulingTests.cpp etlng/TaskManagerTests.cpp @@ -145,7 +147,6 @@ target_sources( util/ConceptsTests.cpp util/CoroutineGroupTests.cpp util/LedgerUtilsTests.cpp - util/StrandedPriorityQueueTests.cpp util/StringHashTests.cpp # Prometheus support util/prometheus/BoolTests.cpp diff --git a/tests/unit/etlng/ETLServiceTests.cpp b/tests/unit/etlng/ETLServiceTests.cpp new file mode 100644 index 000000000..bcdf65f41 --- /dev/null +++ b/tests/unit/etlng/ETLServiceTests.cpp @@ -0,0 +1,341 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025 the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include "data/Types.hpp" +#include "etl/ETLState.hpp" +#include "etl/SystemState.hpp" +#include "etlng/CacheLoaderInterface.hpp" +#include "etlng/CacheUpdaterInterface.hpp" +#include "etlng/ETLService.hpp" +#include "etlng/ExtractorInterface.hpp" +#include "etlng/InitialLoadObserverInterface.hpp" +#include "etlng/LoaderInterface.hpp" +#include "etlng/Models.hpp" +#include "etlng/MonitorInterface.hpp" +#include "etlng/TaskManagerInterface.hpp" +#include "etlng/TaskManagerProviderInterface.hpp" +#include "util/BinaryTestObject.hpp" +#include "util/MockAssert.hpp" +#include "util/MockBackendTestFixture.hpp" +#include "util/MockLedgerPublisher.hpp" +#include "util/MockLoadBalancer.hpp" +#include "util/MockNetworkValidatedLedgers.hpp" +#include "util/MockPrometheus.hpp" +#include "util/MockSubscriptionManager.hpp" +#include "util/TestObject.hpp" +#include "util/async/AnyExecutionContext.hpp" +#include "util/async/context/BasicExecutionContext.hpp" +#include "util/async/context/SyncExecutionContext.hpp" +#include "util/async/impl/ErasedOperation.hpp" +#include "util/config/ConfigDefinition.hpp" +#include "util/config/ConfigValue.hpp" +#include "util/config/Types.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace util::config; + +namespace { +constinit auto const kSEQ = 100; +constinit auto const kLEDGER_HASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652"; + +struct MockMonitor : public etlng::MonitorInterface { + MOCK_METHOD(void, notifyLedgerLoaded, (uint32_t), (override)); + MOCK_METHOD(boost::signals2::scoped_connection, subscribe, (SignalType::slot_type const&), (override)); + MOCK_METHOD(void, run, (std::chrono::steady_clock::duration), (override)); + MOCK_METHOD(void, stop, (), (override)); +}; + +struct MockExtractor : etlng::ExtractorInterface { + MOCK_METHOD(std::optional, extractLedgerWithDiff, (uint32_t), (override)); + MOCK_METHOD(std::optional, extractLedgerOnly, (uint32_t), (override)); +}; + +struct MockLoader : etlng::LoaderInterface { + MOCK_METHOD(void, load, (etlng::model::LedgerData const&), (override)); + MOCK_METHOD(std::optional, loadInitialLedger, (etlng::model::LedgerData const&), (override)); +}; + +struct MockCacheLoader : etlng::CacheLoaderInterface { + MOCK_METHOD(void, load, (uint32_t), (override)); + MOCK_METHOD(void, stop, (), (noexcept, override)); + MOCK_METHOD(void, wait, (), (noexcept, override)); +}; + +struct MockCacheUpdater : etlng::CacheUpdaterInterface { + MOCK_METHOD(void, update, (etlng::model::LedgerData const&), (override)); + MOCK_METHOD(void, update, (uint32_t, std::vector const&), (override)); + MOCK_METHOD(void, update, (uint32_t, std::vector const&), (override)); + MOCK_METHOD(void, setFull, (), (override)); +}; + +struct MockInitialLoadObserver : etlng::InitialLoadObserverInterface { + MOCK_METHOD( + void, + onInitialLoadGotMoreObjects, + (uint32_t, std::vector const&, std::optional), + (override) + ); +}; + +struct MockTaskManager : etlng::TaskManagerInterface { + MOCK_METHOD(void, run, (std::size_t), (override)); + MOCK_METHOD(void, stop, (), (override)); +}; + +struct MockTaskManagerProvider : etlng::TaskManagerProviderInterface { + MOCK_METHOD( + std::unique_ptr, + make, + (util::async::AnyExecutionContext, std::reference_wrapper, uint32_t), + (override) + ); +}; + +auto +createTestData(uint32_t seq) +{ + auto const header = createLedgerHeader(kLEDGER_HASH, seq); + return etlng::model::LedgerData{ + .transactions = {}, + .objects = {util::createObject(), util::createObject(), util::createObject()}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = seq + }; +} +} // namespace + +struct ETLServiceTests : util::prometheus::WithPrometheus, MockBackendTest { + using SameThreadTestContext = util::async::BasicExecutionContext< + util::async::impl::SameThreadContext, + util::async::impl::BasicStopSource, + util::async::impl::SyncDispatchStrategy, + util::async::impl::SystemContextProvider, + util::async::impl::NoErrorHandler>; // This will allow ASSERTs turned exceptions to propagate + +protected: + SameThreadTestContext ctx_; + util::config::ClioConfigDefinition config_{ + {"extractor_threads", ConfigValue{ConfigType::Integer}.defaultValue(4)}, + {"io_threads", ConfigValue{ConfigType::Integer}.defaultValue(2)}, + {"cache.num_diffs", ConfigValue{ConfigType::Integer}.defaultValue(32)}, + {"cache.num_markers", ConfigValue{ConfigType::Integer}.defaultValue(48)}, + {"cache.num_cursors_from_diff", ConfigValue{ConfigType::Integer}.defaultValue(0)}, + {"cache.num_cursors_from_account", ConfigValue{ConfigType::Integer}.defaultValue(0)}, + {"cache.page_fetch_size", ConfigValue{ConfigType::Integer}.defaultValue(512)}, + {"cache.load", ConfigValue{ConfigType::String}.defaultValue("async")} + }; + StrictMockSubscriptionManagerSharedPtr subscriptions_; + std::shared_ptr> balancer_ = + std::make_shared>(); + std::shared_ptr> ledgers_ = + std::make_shared>(); + std::shared_ptr> publisher_ = + std::make_shared>(); + std::shared_ptr> cacheLoader_ = + std::make_shared>(); + std::shared_ptr> cacheUpdater_ = + std::make_shared>(); + std::shared_ptr> extractor_ = std::make_shared>(); + std::shared_ptr> loader_ = std::make_shared>(); + std::shared_ptr> initialLoadObserver_ = + std::make_shared>(); + std::shared_ptr> taskManagerProvider_ = + std::make_shared>(); + std::shared_ptr systemState_ = std::make_shared(); + + etlng::ETLService service_{ + ctx_, + config_, + backend_, + balancer_, + ledgers_, + publisher_, + cacheLoader_, + cacheUpdater_, + extractor_, + loader_, + initialLoadObserver_, + taskManagerProvider_, + systemState_ + }; +}; + +TEST_F(ETLServiceTests, GetInfoWithoutLastPublish) +{ + EXPECT_CALL(*balancer_, toJson()).WillOnce(testing::Return(boost::json::parse(R"json([{"test": "value"}])json"))); + EXPECT_CALL(*publisher_, getLastPublish()).WillOnce(testing::Return(std::chrono::system_clock::time_point{})); + EXPECT_CALL(*publisher_, lastPublishAgeSeconds()).WillRepeatedly(testing::Return(0)); + + auto result = service_.getInfo(); + auto expectedResult = boost::json::parse(R"json({ + "etl_sources": [{"test": "value"}], + "is_writer": 0, + "read_only": 0 + })json"); + + EXPECT_TRUE(result == expectedResult); + EXPECT_FALSE(result.contains("last_publish_age_seconds")); +} + +TEST_F(ETLServiceTests, GetInfoWithLastPublish) +{ + EXPECT_CALL(*balancer_, toJson()).WillOnce(testing::Return(boost::json::parse(R"json([{"test": "value"}])json"))); + EXPECT_CALL(*publisher_, getLastPublish()).WillOnce(testing::Return(std::chrono::system_clock::now())); + EXPECT_CALL(*publisher_, lastPublishAgeSeconds()).WillOnce(testing::Return(42)); + + auto result = service_.getInfo(); + auto expectedResult = boost::json::parse(R"json({ + "etl_sources": [{"test": "value"}], + "is_writer": 0, + "read_only": 0, + "last_publish_age_seconds": "42" + })json"); + + EXPECT_TRUE(result == expectedResult); +} + +TEST_F(ETLServiceTests, IsAmendmentBlocked) +{ + EXPECT_FALSE(service_.isAmendmentBlocked()); +} + +TEST_F(ETLServiceTests, IsCorruptionDetected) +{ + EXPECT_FALSE(service_.isCorruptionDetected()); +} + +TEST_F(ETLServiceTests, GetETLState) +{ + EXPECT_CALL(*balancer_, getETLState()).WillOnce(testing::Return(etl::ETLState{})); + + auto result = service_.getETLState(); + EXPECT_TRUE(result.has_value()); +} + +TEST_F(ETLServiceTests, LastCloseAgeSeconds) +{ + EXPECT_CALL(*publisher_, lastCloseAgeSeconds()).WillOnce(testing::Return(10)); + + auto result = service_.lastCloseAgeSeconds(); + EXPECT_GE(result, 0); +} + +TEST_F(ETLServiceTests, RunWithEmptyDatabase) +{ + auto mockTaskManager = std::make_unique>(); + auto ledgerData = createTestData(kSEQ); + + testing::Sequence s; + EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)).InSequence(s).WillOnce(testing::Return(std::nullopt)); + EXPECT_CALL(*ledgers_, getMostRecent()).WillRepeatedly(testing::Return(kSEQ)); + EXPECT_CALL(*extractor_, extractLedgerOnly(kSEQ)).WillOnce(testing::Return(ledgerData)); + EXPECT_CALL(*balancer_, loadInitialLedger(kSEQ, testing::_, testing::_)) + .WillOnce(testing::Return(std::vector{})); + EXPECT_CALL(*loader_, loadInitialLedger(testing::_)).WillOnce(testing::Return(ripple::LedgerHeader{})); + EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)) + .InSequence(s) + .WillOnce(testing::Return(data::LedgerRange{.minSequence = 1, .maxSequence = kSEQ})); + EXPECT_CALL(*mockTaskManager, run(testing::_)); + EXPECT_CALL(*taskManagerProvider_, make(testing::_, testing::_, kSEQ + 1)) + .WillOnce(testing::Return(std::unique_ptr(mockTaskManager.release()))); + + service_.run(); +} + +TEST_F(ETLServiceTests, RunWithPopulatedDatabase) +{ + auto mockTaskManager = std::make_unique>(); + + EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)) + .WillRepeatedly(testing::Return(data::LedgerRange{.minSequence = 1, .maxSequence = kSEQ})); + EXPECT_CALL(*ledgers_, getMostRecent()).WillRepeatedly(testing::Return(kSEQ)); + EXPECT_CALL(*cacheLoader_, load(kSEQ)); + EXPECT_CALL(*mockTaskManager, run(testing::_)); + EXPECT_CALL(*taskManagerProvider_, make(testing::_, testing::_, kSEQ + 1)) + .WillOnce(testing::Return(std::unique_ptr(mockTaskManager.release()))); + + service_.run(); +} + +TEST_F(ETLServiceTests, WaitForValidatedLedgerIsAborted) +{ + EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)).WillOnce(testing::Return(std::nullopt)); + EXPECT_CALL(*ledgers_, getMostRecent()).Times(2).WillRepeatedly(testing::Return(std::nullopt)); + + // No other calls should happen because we exit early + EXPECT_CALL(*extractor_, extractLedgerOnly(testing::_)).Times(0); + EXPECT_CALL(*balancer_, loadInitialLedger(testing::_, testing::_, testing::_)).Times(0); + EXPECT_CALL(*loader_, loadInitialLedger(testing::_)).Times(0); + EXPECT_CALL(*taskManagerProvider_, make(testing::_, testing::_, testing::_)).Times(0); + + service_.run(); +} + +struct ETLServiceAssertTests : common::util::WithMockAssert, ETLServiceTests {}; + +TEST_F(ETLServiceAssertTests, FailToLoadInitialLedger) +{ + EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)).WillOnce(testing::Return(std::nullopt)); + EXPECT_CALL(*ledgers_, getMostRecent()).WillRepeatedly(testing::Return(kSEQ)); + EXPECT_CALL(*extractor_, extractLedgerOnly(kSEQ)).WillOnce(testing::Return(std::nullopt)); + + // These calls should not happen because loading the initial ledger fails + EXPECT_CALL(*balancer_, loadInitialLedger(testing::_, testing::_, testing::_)).Times(0); + EXPECT_CALL(*loader_, loadInitialLedger(testing::_)).Times(0); + EXPECT_CALL(*taskManagerProvider_, make(testing::_, testing::_, testing::_)).Times(0); + + EXPECT_CLIO_ASSERT_FAIL({ service_.run(); }); +} + +TEST_F(ETLServiceAssertTests, WaitForValidatedLedgerIsAbortedLeadToFailToLoadInitialLedger) +{ + testing::Sequence s; + EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)).WillOnce(testing::Return(std::nullopt)); + EXPECT_CALL(*ledgers_, getMostRecent()).InSequence(s).WillOnce(testing::Return(std::nullopt)); + EXPECT_CALL(*ledgers_, getMostRecent()).InSequence(s).WillOnce(testing::Return(kSEQ)); + + // No other calls should happen because we exit early + EXPECT_CALL(*extractor_, extractLedgerOnly(testing::_)).Times(0); + EXPECT_CALL(*balancer_, loadInitialLedger(testing::_, testing::_, testing::_)).Times(0); + EXPECT_CALL(*loader_, loadInitialLedger(testing::_)).Times(0); + EXPECT_CALL(*taskManagerProvider_, make(testing::_, testing::_, testing::_)).Times(0); + + EXPECT_CLIO_ASSERT_FAIL({ service_.run(); }); +} diff --git a/tests/unit/etlng/LedgerPublisherTests.cpp b/tests/unit/etlng/LedgerPublisherTests.cpp new file mode 100644 index 000000000..fdd32e36e --- /dev/null +++ b/tests/unit/etlng/LedgerPublisherTests.cpp @@ -0,0 +1,357 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include "data/DBHelpers.hpp" +#include "data/Types.hpp" +#include "etl/SystemState.hpp" +#include "etlng/impl/LedgerPublisher.hpp" +#include "util/AsioContextTestFixture.hpp" +#include "util/MockBackendTestFixture.hpp" +#include "util/MockPrometheus.hpp" +#include "util/MockSubscriptionManager.hpp" +#include "util/TestObject.hpp" +#include "util/config/ConfigDefinition.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace testing; +using namespace etlng; +using namespace data; +using namespace std::chrono; + +namespace { + +constexpr auto kACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"; +constexpr auto kACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"; +constexpr auto kLEDGER_HASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652"; +constexpr auto kSEQ = 30; +constexpr auto kAGE = 800; +constexpr auto kAMOUNT = 100; +constexpr auto kFEE = 3; +constexpr auto kFINAL_BALANCE = 110; +constexpr auto kFINAL_BALANCE2 = 30; + +MATCHER_P(ledgerHeaderMatcher, expectedHeader, "Headers match") +{ + return arg.seq == expectedHeader.seq && arg.hash == expectedHeader.hash && + arg.closeTime == expectedHeader.closeTime; +} + +} // namespace + +struct ETLLedgerPublisherNgTest : util::prometheus::WithPrometheus, MockBackendTestStrict, SyncAsioContextTest { + util::config::ClioConfigDefinition cfg{{}}; + StrictMockSubscriptionManagerSharedPtr mockSubscriptionManagerPtr; +}; + +TEST_F(ETLLedgerPublisherNgTest, PublishLedgerHeaderIsWritingFalseAndCacheDisabled) +{ + etl::SystemState dummyState; + dummyState.isWriting = false; + auto const dummyLedgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ, kAGE); + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + publisher.publish(dummyLedgerHeader); + EXPECT_CALL(*backend_, fetchLedgerDiff(kSEQ, _)).Times(0); + + // setLastPublishedSequence not in strand, should verify before run + EXPECT_TRUE(publisher.getLastPublishedSequence()); + EXPECT_EQ(publisher.getLastPublishedSequence().value(), kSEQ); + + ctx_.run(); + EXPECT_TRUE(backend_->fetchLedgerRange()); + EXPECT_EQ(backend_->fetchLedgerRange().value().minSequence, kSEQ); + EXPECT_EQ(backend_->fetchLedgerRange().value().maxSequence, kSEQ); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishLedgerHeaderIsWritingFalseAndCacheEnabled) +{ + etl::SystemState dummyState; + dummyState.isWriting = false; + auto const dummyLedgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ, kAGE); + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + publisher.publish(dummyLedgerHeader); + + // setLastPublishedSequence not in strand, should verify before run + EXPECT_TRUE(publisher.getLastPublishedSequence()); + EXPECT_EQ(publisher.getLastPublishedSequence().value(), kSEQ); + + ctx_.run(); + EXPECT_TRUE(backend_->fetchLedgerRange()); + EXPECT_EQ(backend_->fetchLedgerRange().value().minSequence, kSEQ); + EXPECT_EQ(backend_->fetchLedgerRange().value().maxSequence, kSEQ); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishLedgerHeaderIsWritingTrue) +{ + etl::SystemState dummyState; + dummyState.isWriting = true; + auto const dummyLedgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ, kAGE); + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + publisher.publish(dummyLedgerHeader); + + // setLastPublishedSequence not in strand, should verify before run + EXPECT_TRUE(publisher.getLastPublishedSequence()); + EXPECT_EQ(publisher.getLastPublishedSequence().value(), kSEQ); + + ctx_.run(); + EXPECT_FALSE(backend_->fetchLedgerRange()); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishLedgerHeaderInRange) +{ + etl::SystemState dummyState; + dummyState.isWriting = true; + + auto const dummyLedgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ, 0); // age is 0 + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + backend_->setRange(kSEQ - 1, kSEQ); + + publisher.publish(dummyLedgerHeader); + + // mock fetch fee + EXPECT_CALL(*backend_, doFetchLedgerObject(ripple::keylet::fees().key, kSEQ, _)) + .WillOnce(Return(createLegacyFeeSettingBlob(1, 2, 3, 4, 0))); + + TransactionAndMetadata t1; + t1.transaction = + createPaymentTransactionObject(kACCOUNT, kACCOUNT2, kAMOUNT, kFEE, kSEQ).getSerializer().peekData(); + t1.metadata = createPaymentTransactionMetaObject(kACCOUNT, kACCOUNT2, kFINAL_BALANCE, kFINAL_BALANCE2) + .getSerializer() + .peekData(); + t1.ledgerSequence = kSEQ; + + // mock fetch transactions + EXPECT_CALL(*backend_, fetchAllTransactionsInLedger).WillOnce(Return(std::vector{t1})); + + // setLastPublishedSequence not in strand, should verify before run + EXPECT_TRUE(publisher.getLastPublishedSequence()); + EXPECT_EQ(publisher.getLastPublishedSequence().value(), kSEQ); + + EXPECT_CALL(*mockSubscriptionManagerPtr, pubLedger(_, _, fmt::format("{}-{}", kSEQ - 1, kSEQ), 1)); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubBookChanges); + // mock 1 transaction + EXPECT_CALL(*mockSubscriptionManagerPtr, pubTransaction); + + ctx_.run(); + // last publish time should be set + EXPECT_TRUE(publisher.lastPublishAgeSeconds() <= 1); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishLedgerHeaderCloseTimeGreaterThanNow) +{ + etl::SystemState dummyState; + dummyState.isWriting = true; + + ripple::LedgerHeader dummyLedgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ, 0); + auto const nowPlus10 = system_clock::now() + seconds(10); + auto const closeTime = duration_cast(nowPlus10.time_since_epoch()).count() - kRIPPLE_EPOCH_START; + dummyLedgerHeader.closeTime = ripple::NetClock::time_point{seconds{closeTime}}; + + backend_->setRange(kSEQ - 1, kSEQ); + + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + publisher.publish(dummyLedgerHeader); + + // mock fetch fee + EXPECT_CALL(*backend_, doFetchLedgerObject(ripple::keylet::fees().key, kSEQ, _)) + .WillOnce(Return(createLegacyFeeSettingBlob(1, 2, 3, 4, 0))); + + TransactionAndMetadata t1; + t1.transaction = + createPaymentTransactionObject(kACCOUNT, kACCOUNT2, kAMOUNT, kFEE, kSEQ).getSerializer().peekData(); + t1.metadata = createPaymentTransactionMetaObject(kACCOUNT, kACCOUNT2, kFINAL_BALANCE, kFINAL_BALANCE2) + .getSerializer() + .peekData(); + t1.ledgerSequence = kSEQ; + + // mock fetch transactions + EXPECT_CALL(*backend_, fetchAllTransactionsInLedger(kSEQ, _)) + .WillOnce(Return(std::vector{t1})); + + // setLastPublishedSequence not in strand, should verify before run + EXPECT_TRUE(publisher.getLastPublishedSequence()); + EXPECT_EQ(publisher.getLastPublishedSequence().value(), kSEQ); + + EXPECT_CALL(*mockSubscriptionManagerPtr, pubLedger(_, _, fmt::format("{}-{}", kSEQ - 1, kSEQ), 1)); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubBookChanges); + // mock 1 transaction + EXPECT_CALL(*mockSubscriptionManagerPtr, pubTransaction); + + ctx_.run(); + // last publish time should be set + EXPECT_TRUE(publisher.lastPublishAgeSeconds() <= 1); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishLedgerSeqStopIsTrue) +{ + etl::SystemState dummyState; + dummyState.isStopping = true; + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + EXPECT_FALSE(publisher.publish(kSEQ, {})); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishLedgerSeqMaxAttempt) +{ + etl::SystemState dummyState; + dummyState.isStopping = false; + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + + static constexpr auto kMAX_ATTEMPT = 2; + + LedgerRange const range{.minSequence = kSEQ - 1, .maxSequence = kSEQ - 1}; + EXPECT_CALL(*backend_, hardFetchLedgerRange).Times(kMAX_ATTEMPT).WillRepeatedly(Return(range)); + + EXPECT_FALSE(publisher.publish(kSEQ, kMAX_ATTEMPT, std::chrono::milliseconds{1})); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishLedgerSeqStopIsFalse) +{ + etl::SystemState dummyState; + dummyState.isStopping = false; + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + + LedgerRange const range{.minSequence = kSEQ, .maxSequence = kSEQ}; + EXPECT_CALL(*backend_, hardFetchLedgerRange).WillOnce(Return(range)); + + auto const dummyLedgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ, kAGE); + EXPECT_CALL(*backend_, fetchLedgerBySequence(kSEQ, _)).WillOnce(Return(dummyLedgerHeader)); + + EXPECT_TRUE(publisher.publish(kSEQ, {})); + ctx_.run(); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishMultipleTxInOrder) +{ + etl::SystemState dummyState; + dummyState.isWriting = true; + + auto const dummyLedgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ, 0); // age is 0 + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + backend_->setRange(kSEQ - 1, kSEQ); + + publisher.publish(dummyLedgerHeader); + + // mock fetch fee + EXPECT_CALL(*backend_, doFetchLedgerObject(ripple::keylet::fees().key, kSEQ, _)) + .WillOnce(Return(createLegacyFeeSettingBlob(1, 2, 3, 4, 0))); + + // t1 index > t2 index + TransactionAndMetadata t1; + t1.transaction = + createPaymentTransactionObject(kACCOUNT, kACCOUNT2, kAMOUNT, kFEE, kSEQ).getSerializer().peekData(); + t1.metadata = createPaymentTransactionMetaObject(kACCOUNT, kACCOUNT2, kFINAL_BALANCE, kFINAL_BALANCE2, 2) + .getSerializer() + .peekData(); + t1.ledgerSequence = kSEQ; + t1.date = 1; + TransactionAndMetadata t2; + t2.transaction = + createPaymentTransactionObject(kACCOUNT, kACCOUNT2, kAMOUNT, kFEE, kSEQ).getSerializer().peekData(); + t2.metadata = createPaymentTransactionMetaObject(kACCOUNT, kACCOUNT2, kFINAL_BALANCE, kFINAL_BALANCE2, 1) + .getSerializer() + .peekData(); + t2.ledgerSequence = kSEQ; + t2.date = 2; + + // mock fetch transactions + EXPECT_CALL(*backend_, fetchAllTransactionsInLedger(kSEQ, _)) + .WillOnce(Return(std::vector{t1, t2})); + + // setLastPublishedSequence not in strand, should verify before run + EXPECT_TRUE(publisher.getLastPublishedSequence()); + EXPECT_EQ(publisher.getLastPublishedSequence().value(), kSEQ); + + EXPECT_CALL(*mockSubscriptionManagerPtr, pubLedger(_, _, fmt::format("{}-{}", kSEQ - 1, kSEQ), 2)); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubBookChanges); + // should call pubTransaction t2 first (greater tx index) + Sequence const s; + EXPECT_CALL(*mockSubscriptionManagerPtr, pubTransaction(t2, _)).InSequence(s); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubTransaction(t1, _)).InSequence(s); + + ctx_.run(); + // last publish time should be set + EXPECT_TRUE(publisher.lastPublishAgeSeconds() <= 1); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishVeryOldLedgerShouldSkip) +{ + etl::SystemState dummyState; + dummyState.isWriting = true; + + // Create a ledger header with age (800) greater than MAX_LEDGER_AGE_SECONDS (600) + auto const dummyLedgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ, 800); + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + backend_->setRange(kSEQ - 1, kSEQ); + + publisher.publish(dummyLedgerHeader); + + EXPECT_CALL(*mockSubscriptionManagerPtr, pubLedger).Times(0); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubBookChanges).Times(0); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubTransaction).Times(0); + + EXPECT_TRUE(publisher.getLastPublishedSequence()); + EXPECT_EQ(publisher.getLastPublishedSequence().value(), kSEQ); + + ctx_.run(); +} + +TEST_F(ETLLedgerPublisherNgTest, PublishMultipleLedgersInQuickSuccession) +{ + etl::SystemState dummyState; + dummyState.isWriting = true; + + auto const dummyLedgerHeader1 = createLedgerHeader(kLEDGER_HASH, kSEQ, 0); + auto const dummyLedgerHeader2 = createLedgerHeader(kLEDGER_HASH, kSEQ + 1, 0); + impl::LedgerPublisher publisher(ctx_, backend_, mockSubscriptionManagerPtr, dummyState); + backend_->setRange(kSEQ - 1, kSEQ + 1); + + // Publish two ledgers in quick succession + publisher.publish(dummyLedgerHeader1); + publisher.publish(dummyLedgerHeader2); + + EXPECT_CALL(*backend_, doFetchLedgerObject(ripple::keylet::fees().key, kSEQ, _)) + .WillOnce(Return(createLegacyFeeSettingBlob(1, 2, 3, 4, 0))); + EXPECT_CALL(*backend_, doFetchLedgerObject(ripple::keylet::fees().key, kSEQ + 1, _)) + .WillOnce(Return(createLegacyFeeSettingBlob(1, 2, 3, 4, 0))); + + EXPECT_CALL(*backend_, fetchAllTransactionsInLedger(kSEQ, _)) + .WillOnce(Return(std::vector{})); + EXPECT_CALL(*backend_, fetchAllTransactionsInLedger(kSEQ + 1, _)) + .WillOnce(Return(std::vector{})); + + Sequence const s; + EXPECT_CALL(*mockSubscriptionManagerPtr, pubLedger(ledgerHeaderMatcher(dummyLedgerHeader1), _, _, _)).InSequence(s); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubBookChanges(ledgerHeaderMatcher(dummyLedgerHeader1), _)).InSequence(s); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubLedger(ledgerHeaderMatcher(dummyLedgerHeader2), _, _, _)).InSequence(s); + EXPECT_CALL(*mockSubscriptionManagerPtr, pubBookChanges(ledgerHeaderMatcher(dummyLedgerHeader2), _)).InSequence(s); + + EXPECT_TRUE(publisher.getLastPublishedSequence()); + EXPECT_EQ(publisher.getLastPublishedSequence().value(), kSEQ + 1); + + ctx_.run(); +} diff --git a/tests/unit/etlng/LoadingTests.cpp b/tests/unit/etlng/LoadingTests.cpp index 1680d39d2..512c8968d 100644 --- a/tests/unit/etlng/LoadingTests.cpp +++ b/tests/unit/etlng/LoadingTests.cpp @@ -64,13 +64,10 @@ struct MockLoadObserver : etlng::InitialLoadObserverInterface { ); }; -struct LoadingTests : util::prometheus::WithPrometheus, - MockBackendTest, - MockLedgerFetcherTest, - MockAmendmentBlockHandlerTest { +struct LoadingTests : util::prometheus::WithPrometheus, MockBackendTest, MockAmendmentBlockHandlerTest { protected: std::shared_ptr mockRegistryPtr_ = std::make_shared(); - Loader loader_{backend_, mockLedgerFetcherPtr_, mockRegistryPtr_, mockAmendmentBlockHandlerPtr_}; + Loader loader_{backend_, mockRegistryPtr_, mockAmendmentBlockHandlerPtr_}; }; struct LoadingAssertTest : common::util::WithMockAssert, LoadingTests {}; @@ -148,6 +145,33 @@ TEST_F(LoadingTests, OnInitialLoadGotMoreObjectsWithoutKey) loader_.onInitialLoadGotMoreObjects(kSEQ, data.objects, lastKey); } +TEST_F(LoadingTests, OnInitialLoadGotMoreObjectsFailure) +{ + auto const data = createTestData(); + auto const lastKey = std::optional{}; + + EXPECT_CALL(*mockRegistryPtr_, dispatchInitialObjects(kSEQ, data.objects, std::string{})) + .WillOnce([](auto, auto, auto) { throw std::runtime_error("some error"); }); + EXPECT_CALL(*mockAmendmentBlockHandlerPtr_, notifyAmendmentBlocked()); + + loader_.onInitialLoadGotMoreObjects(kSEQ, data.objects, lastKey); +} + +TEST_F(LoadingTests, LoadInitialLedgerFailure) +{ + auto const data = createTestData(); + + EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)).WillOnce(testing::Return(std::nullopt)); + EXPECT_CALL(*backend_, doFinishWrites()).Times(0); + EXPECT_CALL(*mockRegistryPtr_, dispatchInitialData(data)).WillOnce([](auto const&) { + throw std::runtime_error("some error"); + }); + EXPECT_CALL(*mockAmendmentBlockHandlerPtr_, notifyAmendmentBlocked()); + + auto const res = loader_.loadInitialLedger(data); + EXPECT_FALSE(res.has_value()); +} + TEST_F(LoadingAssertTest, LoadInitialLedgerHasDataInDB) { auto const data = createTestData(); diff --git a/tests/unit/etlng/MonitorTests.cpp b/tests/unit/etlng/MonitorTests.cpp index 5c07216e8..e719a1718 100644 --- a/tests/unit/etlng/MonitorTests.cpp +++ b/tests/unit/etlng/MonitorTests.cpp @@ -66,7 +66,7 @@ TEST_F(MonitorTests, ConsumesAndNotifiesForAllOutstandingSequencesAtOnce) }); auto subscription = monitor_.subscribe(actionMock_.AsStdFunction()); - monitor_.run(std::chrono::milliseconds{1}); + monitor_.run(std::chrono::milliseconds{10}); unblock.acquire(); } @@ -111,3 +111,18 @@ TEST_F(MonitorTests, NotifiesWhenForcedByNewSequenceAvailableFromNetwork) pusher(kSTART_SEQ); // pretend network validated a new ledger unblock.acquire(); } + +TEST_F(MonitorTests, NotifiesWhenForcedByLedgerLoaded) +{ + LedgerRange const range(kSTART_SEQ, kSTART_SEQ); + std::binary_semaphore unblock(0); + + EXPECT_CALL(*ledgers_, subscribe(testing::_)); + EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)).WillOnce(testing::Return(range)); + EXPECT_CALL(actionMock_, Call).WillOnce([&] { unblock.release(); }); + + auto subscription = monitor_.subscribe(actionMock_.AsStdFunction()); + monitor_.run(std::chrono::seconds{10}); // expected to be force-invoked sooner than in 10 sec + monitor_.notifyLedgerLoaded(kSTART_SEQ); // notify about newly committed ledger + unblock.acquire(); +} diff --git a/tests/unit/etlng/RegistryTests.cpp b/tests/unit/etlng/RegistryTests.cpp index 3e01ca7ac..5e40a6c2a 100644 --- a/tests/unit/etlng/RegistryTests.cpp +++ b/tests/unit/etlng/RegistryTests.cpp @@ -17,16 +17,21 @@ */ //============================================================================== +#include "etl/SystemState.hpp" #include "etlng/Models.hpp" +#include "etlng/MonitorInterface.hpp" #include "etlng/impl/Registry.hpp" #include "util/BinaryTestObject.hpp" #include "util/LoggerFixtures.hpp" +#include "util/MockPrometheus.hpp" #include "util/TestObject.hpp" +#include #include #include #include +#include #include #include #include @@ -176,7 +181,88 @@ struct MockExtNftOffer { MOCK_METHOD(void, onInitialTransaction, (uint32_t, etlng::model::Transaction const&), (const)); }; -struct RegistryTest : NoLoggerFixture {}; +// Mock extensions with allowInReadonly +struct MockExtLedgerDataReadonly { + MOCK_METHOD(void, onLedgerData, (etlng::model::LedgerData const&), (const)); + + static bool + allowInReadonly() + { + return true; + } +}; + +struct MockExtInitialDataReadonly { + MOCK_METHOD(void, onInitialData, (etlng::model::LedgerData const&), (const)); + + static bool + allowInReadonly() + { + return true; + } +}; + +struct MockExtOnObjectReadonly { + MOCK_METHOD(void, onObject, (uint32_t, etlng::model::Object const&), (const)); + + static bool + allowInReadonly() + { + return true; + } +}; + +struct MockExtTransactionNftBurnReadonly { + using spec = etlng::model::Spec; + MOCK_METHOD(void, onTransaction, (uint32_t, etlng::model::Transaction const&), (const)); + + static bool + allowInReadonly() + { + return true; + } +}; + +struct MockExtInitialObjectReadonly { + MOCK_METHOD(void, onInitialObject, (uint32_t, etlng::model::Object const&), (const)); + + static bool + allowInReadonly() + { + return true; + } +}; + +struct MockExtInitialObjectsReadonly { + MOCK_METHOD(void, onInitialObjects, (uint32_t, std::vector const&, std::string), (const)); + + static bool + allowInReadonly() + { + return true; + } +}; + +struct MockExtNftBurnReadonly { + using spec = etlng::model::Spec; + MOCK_METHOD(void, onInitialTransaction, (uint32_t, etlng::model::Transaction const&), (const)); + + static bool + allowInReadonly() + { + return true; + } +}; + +struct RegistryTest : NoLoggerFixture, util::prometheus::WithPrometheus { + RegistryTest() + { + state_.isWriting = true; + } + +protected: + etl::SystemState state_{}; +}; } // namespace @@ -195,7 +281,7 @@ TEST_F(RegistryTest, FilteringOfTxWorksCorrectlyForInitialTransaction) EXPECT_CALL(extOffer, onInitialTransaction(testing::_, testing::_)); // 1 create offer auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); - auto reg = Registry(extBurn, extOffer); + auto reg = Registry(state_, extBurn, extOffer); reg.dispatchInitialData(etlng::model::LedgerData{ .transactions = transactions, .objects = {}, @@ -222,7 +308,7 @@ TEST_F(RegistryTest, FilteringOfTxWorksCorrectlyForTransaction) EXPECT_CALL(extOffer, onTransaction(testing::_, testing::_)); // 1 create offer auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); - auto reg = Registry(extBurn, extOffer); + auto reg = Registry(state_, extBurn, extOffer); reg.dispatch(etlng::model::LedgerData{ .transactions = std::move(transactions), .objects = {}, @@ -242,7 +328,7 @@ TEST_F(RegistryTest, InitialObjectsEmpty) EXPECT_CALL(extObj, onInitialObject(testing::_, testing::_)).Times(0); // 0 empty objects sent EXPECT_CALL(extObjs, onInitialObjects(testing::_, testing::_, testing::_)); // 1 vector passed as is - auto reg = Registry(extObj, extObjs); + auto reg = Registry(state_, extObj, extObjs); reg.dispatchInitialObjects(kSEQ, {}, {}); } @@ -254,7 +340,7 @@ TEST_F(RegistryTest, InitialObjectsDispatched) EXPECT_CALL(extObj, onInitialObject(testing::_, testing::_)).Times(3); // 3 objects sent EXPECT_CALL(extObjs, onInitialObjects(testing::_, testing::_, testing::_)); // 1 vector passed as is - auto reg = Registry(extObj, extObjs); + auto reg = Registry(state_, extObj, extObjs); reg.dispatchInitialObjects(kSEQ, {util::createObject(), util::createObject(), util::createObject()}, {}); } @@ -265,7 +351,7 @@ TEST_F(RegistryTest, ObjectsDispatched) EXPECT_CALL(extObj, onObject(testing::_, testing::_)).Times(3); // 3 objects sent auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); - auto reg = Registry(extObj); + auto reg = Registry(state_, extObj); reg.dispatch(etlng::model::LedgerData{ .transactions = {}, .objects = {util::createObject(), util::createObject(), util::createObject()}, @@ -290,7 +376,7 @@ TEST_F(RegistryTest, OnLedgerDataForBatch) EXPECT_CALL(ext, onLedgerData(testing::_)); // 1 batch (dispatch call) auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); - auto reg = Registry(ext); + auto reg = Registry(state_, ext); reg.dispatch(etlng::model::LedgerData{ .transactions = std::move(transactions), .objects = {}, @@ -311,7 +397,7 @@ TEST_F(RegistryTest, InitialObjectsCorrectOrderOfHookCalls) EXPECT_CALL(extObjs, onInitialObjects); EXPECT_CALL(extObj, onInitialObject).Times(3); - auto reg = Registry(extObj, extObjs); + auto reg = Registry(state_, extObj, extObjs); reg.dispatchInitialObjects(kSEQ, {util::createObject(), util::createObject(), util::createObject()}, {}); } @@ -331,7 +417,7 @@ TEST_F(RegistryTest, InitialDataCorrectOrderOfHookCalls) EXPECT_CALL(extInitialTransaction, onInitialTransaction).Times(2); auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); - auto reg = Registry(extInitialTransaction, extInitialData); + auto reg = Registry(state_, extInitialTransaction, extInitialData); reg.dispatchInitialData(etlng::model::LedgerData{ .transactions = std::move(transactions), .objects = {}, @@ -368,7 +454,7 @@ TEST_F(RegistryTest, LedgerDataCorrectOrderOfHookCalls) auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); auto reg = Registry( - extOnObject, extOnTransaction, extLedgerData + state_, extOnObject, extOnTransaction, extLedgerData ); reg.dispatch(etlng::model::LedgerData{ .transactions = std::move(transactions), @@ -380,3 +466,362 @@ TEST_F(RegistryTest, LedgerDataCorrectOrderOfHookCalls) .seq = kSEQ }); } + +TEST_F(RegistryTest, ReadonlyModeLedgerDataAllowed) +{ + auto transactions = std::vector{ + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + }; + + auto ext = MockExtLedgerDataReadonly{}; + state_.isWriting = false; + + EXPECT_CALL(ext, onLedgerData(testing::_)); + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, ext); + reg.dispatch(etlng::model::LedgerData{ + .transactions = std::move(transactions), + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); +} + +TEST_F(RegistryTest, ReadonlyModeTransactionAllowed) +{ + auto transactions = std::vector{ + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + }; + + auto extTx = MockExtTransactionNftBurnReadonly{}; + state_.isWriting = false; + + EXPECT_CALL(extTx, onTransaction(testing::_, testing::_)).Times(2); + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, extTx); + reg.dispatch(etlng::model::LedgerData{ + .transactions = std::move(transactions), + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); +} + +TEST_F(RegistryTest, ReadonlyModeObjectAllowed) +{ + auto objects = std::vector{ + util::createObject(), + util::createObject(), + util::createObject(), + }; + + auto extObj = MockExtOnObjectReadonly{}; + state_.isWriting = false; + + EXPECT_CALL(extObj, onObject(testing::_, testing::_)).Times(3); + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, extObj); + reg.dispatch(etlng::model::LedgerData{ + .transactions = {}, + .objects = std::move(objects), + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); +} + +TEST_F(RegistryTest, ReadonlyModeInitialDataAllowed) +{ + auto transactions = std::vector{ + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + }; + + auto extInitialData = MockExtInitialDataReadonly{}; + state_.isWriting = false; + + EXPECT_CALL(extInitialData, onInitialData(testing::_)); + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, extInitialData); + reg.dispatchInitialData(etlng::model::LedgerData{ + .transactions = std::move(transactions), + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); +} + +TEST_F(RegistryTest, ReadonlyModeInitialTransactionAllowed) +{ + auto transactions = std::vector{ + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + }; + + auto extTx = MockExtNftBurnReadonly{}; + state_.isWriting = false; + + EXPECT_CALL(extTx, onInitialTransaction(testing::_, testing::_)).Times(2); + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, extTx); + reg.dispatchInitialData(etlng::model::LedgerData{ + .transactions = std::move(transactions), + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); +} + +TEST_F(RegistryTest, ReadonlyModeInitialObjectAllowed) +{ + auto extObj = MockExtInitialObjectReadonly{}; + state_.isWriting = false; + + EXPECT_CALL(extObj, onInitialObject(testing::_, testing::_)).Times(3); + + auto reg = Registry(state_, extObj); + reg.dispatchInitialObjects(kSEQ, {util::createObject(), util::createObject(), util::createObject()}, {}); +} + +TEST_F(RegistryTest, ReadonlyModeInitialObjectsAllowed) +{ + auto extObjs = MockExtInitialObjectsReadonly{}; + state_.isWriting = false; + + EXPECT_CALL(extObjs, onInitialObjects(testing::_, testing::_, testing::_)); + + auto reg = Registry(state_, extObjs); + reg.dispatchInitialObjects(kSEQ, {util::createObject(), util::createObject(), util::createObject()}, {}); +} + +TEST_F(RegistryTest, ReadonlyModeRegularExtensionsNotCalled) +{ + auto extLedgerData = MockExtLedgerData{}; // No allowInReadonly method + auto objects = std::vector{ + util::createObject(), + util::createObject(), + util::createObject(), + }; + + state_.isWriting = false; + + EXPECT_CALL(extLedgerData, onLedgerData(testing::_)).Times(0); // Should NOT be called in readonly mode + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, extLedgerData); + reg.dispatch(etlng::model::LedgerData{ + .transactions = {}, + .objects = std::move(objects), + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); +} + +TEST_F(RegistryTest, MixedReadonlyAndRegularExtensions) +{ + auto extReadonly = MockExtLedgerDataReadonly{}; + auto extRegular = MockExtLedgerData{}; + auto objects = std::vector{ + util::createObject(), + util::createObject(), + util::createObject(), + }; + + state_.isWriting = false; + + EXPECT_CALL(extReadonly, onLedgerData(testing::_)); + EXPECT_CALL(extRegular, onLedgerData(testing::_)).Times(0); // Should NOT be called in readonly mode + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, extReadonly, extRegular); + reg.dispatch(etlng::model::LedgerData{ + .transactions = {}, + .objects = std::move(objects), + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); +} + +TEST_F(RegistryTest, MonitorInterfaceExecution) +{ + struct MockMonitor : etlng::MonitorInterface { + MOCK_METHOD(void, notifyLedgerLoaded, (uint32_t), (override)); + MOCK_METHOD(boost::signals2::scoped_connection, subscribe, (SignalType::slot_type const&), (override)); + MOCK_METHOD(void, run, (std::chrono::steady_clock::duration), (override)); + MOCK_METHOD(void, stop, (), (override)); + }; + + auto monitor = MockMonitor{}; + EXPECT_CALL(monitor, notifyLedgerLoaded(kSEQ)).Times(1); + + monitor.notifyLedgerLoaded(kSEQ); +} + +TEST_F(RegistryTest, ReadonlyModeWithAllowInReadonlyTest) +{ + struct ExtWithAllowInReadonly { + MOCK_METHOD(void, onLedgerData, (etlng::model::LedgerData const&), (const)); + + static bool + allowInReadonly() + { + return true; + } + }; + + auto ext = ExtWithAllowInReadonly{}; + state_.isWriting = false; + + EXPECT_CALL(ext, onLedgerData(testing::_)).Times(1); + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, ext); + reg.dispatch(etlng::model::LedgerData{ + .transactions = {}, + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); +} + +TEST_F(RegistryTest, ReadonlyModeExecutePluralHooksIfAllowedPaths) +{ + struct ExtWithBothHooksAndAllowReadonly { + MOCK_METHOD(void, onLedgerData, (etlng::model::LedgerData const&), (const)); + MOCK_METHOD(void, onInitialData, (etlng::model::LedgerData const&), (const)); + MOCK_METHOD(void, onInitialObjects, (uint32_t, std::vector const&, std::string), (const)); + + static bool + allowInReadonly() + { + return true; + } + }; + + auto ext = ExtWithBothHooksAndAllowReadonly{}; + state_.isWriting = false; + + auto transactions = std::vector{ + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + }; + auto objects = std::vector{ + util::createObject(), + }; + + EXPECT_CALL(ext, onLedgerData(testing::_)).Times(1); + EXPECT_CALL(ext, onInitialData(testing::_)).Times(1); + EXPECT_CALL(ext, onInitialObjects(testing::_, testing::_, testing::_)).Times(1); + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, ext); + + reg.dispatch(etlng::model::LedgerData{ + .transactions = transactions, + .objects = objects, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); + + reg.dispatchInitialData(etlng::model::LedgerData{ + .transactions = std::move(transactions), + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); + + reg.dispatchInitialObjects(kSEQ, objects, {}); +} + +TEST_F(RegistryTest, ReadonlyModeExecuteByOneHooksIfAllowedPaths) +{ + struct ExtWithBothHooksAndAllowReadonly { + using spec = etlng::model::Spec; + + MOCK_METHOD(void, onObject, (uint32_t, etlng::model::Object const&), (const)); + MOCK_METHOD(void, onInitialObject, (uint32_t, etlng::model::Object const&), (const)); + MOCK_METHOD(void, onTransaction, (uint32_t, etlng::model::Transaction const&), (const)); + MOCK_METHOD(void, onInitialTransaction, (uint32_t, etlng::model::Transaction const&), (const)); + + static bool + allowInReadonly() + { + return true; + } + }; + + auto ext = ExtWithBothHooksAndAllowReadonly{}; + state_.isWriting = false; + + auto transactions = std::vector{ + util::createTransaction(ripple::TxType::ttNFTOKEN_BURN), + }; + auto objects = std::vector{ + util::createObject(), + }; + + EXPECT_CALL(ext, onTransaction(testing::_, testing::_)).Times(1); + EXPECT_CALL(ext, onObject(testing::_, testing::_)).Times(1); + EXPECT_CALL(ext, onInitialTransaction(testing::_, testing::_)).Times(1); + EXPECT_CALL(ext, onInitialObject(testing::_, testing::_)).Times(1); + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + auto reg = Registry(state_, ext); + + reg.dispatch(etlng::model::LedgerData{ + .transactions = transactions, + .objects = objects, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); + + reg.dispatchInitialData(etlng::model::LedgerData{ + .transactions = std::move(transactions), + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }); + + reg.dispatchInitialObjects(kSEQ, objects, {}); +} diff --git a/tests/unit/etlng/TaskManagerTests.cpp b/tests/unit/etlng/TaskManagerTests.cpp index cbeaa7523..d94655a0b 100644 --- a/tests/unit/etlng/TaskManagerTests.cpp +++ b/tests/unit/etlng/TaskManagerTests.cpp @@ -20,6 +20,7 @@ #include "etlng/ExtractorInterface.hpp" #include "etlng/LoaderInterface.hpp" #include "etlng/Models.hpp" +#include "etlng/MonitorInterface.hpp" #include "etlng/SchedulerInterface.hpp" #include "etlng/impl/Loading.hpp" #include "etlng/impl/TaskManager.hpp" @@ -29,11 +30,13 @@ #include "util/async/AnyExecutionContext.hpp" #include "util/async/context/BasicExecutionContext.hpp" +#include #include #include #include #include +#include #include #include #include @@ -63,18 +66,27 @@ struct MockLoader : etlng::LoaderInterface { MOCK_METHOD(std::optional, loadInitialLedger, (LedgerData const&), (override)); }; +struct MockMonitor : etlng::MonitorInterface { + MOCK_METHOD(void, notifyLedgerLoaded, (uint32_t), (override)); + MOCK_METHOD(boost::signals2::scoped_connection, subscribe, (SignalType::slot_type const&), (override)); + MOCK_METHOD(void, run, (std::chrono::steady_clock::duration), (override)); + MOCK_METHOD(void, stop, (), (override)); +}; + struct TaskManagerTests : NoLoggerFixture { using MockSchedulerType = testing::NiceMock; using MockExtractorType = testing::NiceMock; using MockLoaderType = testing::NiceMock; + using MockMonitorType = testing::NiceMock; protected: util::async::CoroExecutionContext ctx_{2}; std::shared_ptr mockSchedulerPtr_ = std::make_shared(); std::shared_ptr mockExtractorPtr_ = std::make_shared(); std::shared_ptr mockLoaderPtr_ = std::make_shared(); + std::shared_ptr mockMonitorPtr_ = std::make_shared(); - TaskManager taskManager_{ctx_, *mockSchedulerPtr_, *mockExtractorPtr_, *mockLoaderPtr_}; + TaskManager taskManager_{ctx_, mockSchedulerPtr_, *mockExtractorPtr_, *mockLoaderPtr_, *mockMonitorPtr_, kSEQ}; }; auto @@ -97,8 +109,7 @@ createTestData(uint32_t seq) TEST_F(TaskManagerTests, LoaderGetsDataIfNextSequenceIsExtracted) { static constexpr auto kTOTAL = 64uz; - static constexpr auto kEXTRACTORS = 5uz; - static constexpr auto kLOADERS = 1uz; + static constexpr auto kEXTRACTORS = 4uz; std::atomic_uint32_t seq = kSEQ; std::vector loaded; @@ -118,17 +129,16 @@ TEST_F(TaskManagerTests, LoaderGetsDataIfNextSequenceIsExtracted) EXPECT_CALL(*mockLoaderPtr_, load(testing::_)).Times(kTOTAL).WillRepeatedly([&](LedgerData data) { loaded.push_back(data.seq); - if (loaded.size() == kTOTAL) { done.release(); } }); - auto loop = ctx_.execute([&] { taskManager_.run({.numExtractors = kEXTRACTORS, .numLoaders = kLOADERS}); }); - done.acquire(); + EXPECT_CALL(*mockMonitorPtr_, notifyLedgerLoaded(testing::_)).Times(kTOTAL); + taskManager_.run(kEXTRACTORS); + done.acquire(); taskManager_.stop(); - loop.wait(); EXPECT_EQ(loaded.size(), kTOTAL); for (std::size_t i = 0; i < loaded.size(); ++i) { diff --git a/tests/unit/etlng/ext/CacheTests.cpp b/tests/unit/etlng/ext/CacheTests.cpp index c1697b6d1..5fa83399f 100644 --- a/tests/unit/etlng/ext/CacheTests.cpp +++ b/tests/unit/etlng/ext/CacheTests.cpp @@ -18,8 +18,10 @@ //============================================================================== #include "etlng/Models.hpp" +#include "etlng/impl/CacheUpdater.hpp" #include "etlng/impl/ext/Cache.hpp" #include "util/BinaryTestObject.hpp" +#include "util/LoggerFixtures.hpp" #include "util/MockLedgerCache.hpp" #include "util/MockPrometheus.hpp" #include "util/TestObject.hpp" @@ -27,6 +29,7 @@ #include #include +#include #include #include @@ -56,10 +59,11 @@ createTestData() } // namespace -struct CacheExtTests : util::prometheus::WithPrometheus { +struct CacheExtTests : NoLoggerFixture, util::prometheus::WithPrometheus { protected: MockLedgerCache cache_; - etlng::impl::CacheExt ext_{cache_}; + std::shared_ptr updater_ = std::make_shared(cache_); + etlng::impl::CacheExt ext_{updater_}; }; TEST_F(CacheExtTests, OnLedgerDataUpdatesCache) @@ -89,3 +93,8 @@ TEST_F(CacheExtTests, OnInitialObjectsUpdateCache) ext_.onInitialObjects(kSEQ, objects, kUNUSED_LAST_KEY); } + +TEST_F(CacheExtTests, AllowInReadonlyReturnsTrue) +{ + EXPECT_TRUE(ext_.allowInReadonly()); +} diff --git a/tests/unit/util/StrandedPriorityQueueTests.cpp b/tests/unit/util/StrandedPriorityQueueTests.cpp deleted file mode 100644 index 00703321c..000000000 --- a/tests/unit/util/StrandedPriorityQueueTests.cpp +++ /dev/null @@ -1,195 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of clio: https://github.com/XRPLF/clio - Copyright (c) 2025, the clio developers. - - Permission to use, copy, modify, and distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include "util/StrandedPriorityQueue.hpp" -#include "util/async/AnyExecutionContext.hpp" -#include "util/async/AnyOperation.hpp" -#include "util/async/context/BasicExecutionContext.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -using namespace util; - -namespace { - -struct TestData { - uint32_t seq; - - auto - operator<=>(TestData const&) const = default; -}; - -} // namespace - -TEST(StrandedPriorityQueueTests, DefaultPriority) -{ - util::async::CoroExecutionContext ctx; - StrandedPriorityQueue queue{ctx.makeStrand()}; - - for (auto i = 0u; i < 100u; ++i) { - EXPECT_TRUE(queue.enqueue(TestData{.seq = i})); - } - - EXPECT_FALSE(queue.empty()); - - auto next = 99u; - while (auto maybeValue = queue.dequeue()) { - EXPECT_EQ(maybeValue->seq, next--); - } - - EXPECT_TRUE(queue.empty()); -} - -TEST(StrandedPriorityQueueTests, CustomPriority) -{ - struct Comp { - [[nodiscard]] bool - operator()(TestData const& lhs, TestData const& rhs) const noexcept - { - return lhs.seq > rhs.seq; - } - }; - - util::async::CoroExecutionContext ctx; - StrandedPriorityQueue queue{ctx.makeStrand()}; - - for (auto i = 0u; i < 100u; ++i) { - EXPECT_TRUE(queue.enqueue(TestData{.seq = i})); - } - - EXPECT_FALSE(queue.empty()); - - auto next = 0u; - while (auto maybeValue = queue.dequeue()) { - EXPECT_EQ(maybeValue->seq, next++); - } - - EXPECT_TRUE(queue.empty()); -} - -TEST(StrandedPriorityQueueTests, MultipleThreadsUnlimitedQueue) -{ - async::CoroExecutionContext realCtx{6}; - async::AnyExecutionContext ctx{realCtx}; - StrandedPriorityQueue queue{ctx.makeStrand()}; - - EXPECT_TRUE(queue.empty()); - static constexpr auto kTOTAL_THREADS = 5u; - static constexpr auto kTOTAL_ITEMS_PER_THREAD = 100u; - - std::atomic_size_t totalEnqueued = 0uz; - std::vector> tasks; - tasks.reserve(kTOTAL_THREADS); - - for (auto batchIdx = 0u; batchIdx < kTOTAL_THREADS; ++batchIdx) { - // enqueue batches tasks running on multiple threads - tasks.push_back(ctx.execute([&queue, batchIdx, &totalEnqueued] { - for (auto i = 0u; i < kTOTAL_ITEMS_PER_THREAD; ++i) { - if (queue.enqueue(TestData{.seq = (batchIdx * kTOTAL_ITEMS_PER_THREAD) + i})) - ++totalEnqueued; - } - })); - } - - for (auto& task : tasks) - task.wait(); - - auto next = (kTOTAL_ITEMS_PER_THREAD * kTOTAL_THREADS) - 1; - while (auto maybeValue = queue.dequeue()) { - EXPECT_EQ(maybeValue->seq, next--); - } - - EXPECT_TRUE(queue.empty()); - EXPECT_EQ(totalEnqueued, kTOTAL_ITEMS_PER_THREAD * kTOTAL_THREADS); -} - -TEST(StrandedPriorityQueueTests, MultipleThreadsLimitedQueue) -{ - static constexpr auto kQUEUE_SIZE_LIMIT = 32uz; - static constexpr auto kTOTAL_THREADS = 5u; - static constexpr auto kTOTAL_ITEMS_PER_THREAD = 100u; - - async::CoroExecutionContext realCtx{8}; - async::AnyExecutionContext ctx{realCtx}; - StrandedPriorityQueue queue{ctx.makeStrand(), kQUEUE_SIZE_LIMIT}; - - EXPECT_TRUE(queue.empty()); - - std::atomic_size_t totalEnqueued = 0uz; - std::atomic_size_t totalSleepCycles = 0uz; - std::vector> tasks; - tasks.reserve(kTOTAL_THREADS); - - std::unordered_set expectedSequences; - - for (auto batchIdx = 0u; batchIdx < kTOTAL_THREADS; ++batchIdx) { - for (auto i = 0u; i < kTOTAL_ITEMS_PER_THREAD; ++i) { - expectedSequences.insert((batchIdx * kTOTAL_ITEMS_PER_THREAD) + i); - } - - // enqueue batches tasks running on multiple threads - tasks.push_back(ctx.execute([&queue, batchIdx, &totalEnqueued, &totalSleepCycles] { - for (auto i = 0u; i < kTOTAL_ITEMS_PER_THREAD; ++i) { - auto data = TestData{.seq = (batchIdx * kTOTAL_ITEMS_PER_THREAD) + i}; - while (not queue.enqueue(data)) { - std::this_thread::sleep_for(std::chrono::nanoseconds{1}); - ++totalSleepCycles; - } - ++totalEnqueued; - } - })); - } - - EXPECT_FALSE(expectedSequences.empty()); - - auto loader = ctx.execute([&queue, &expectedSequences] { - while (not expectedSequences.empty()) { - while (auto maybeValue = queue.dequeue()) { - EXPECT_TRUE(expectedSequences.contains(maybeValue->seq)); - expectedSequences.erase(maybeValue->seq); - } - } - }); - - for (auto& task : tasks) - task.wait(); - loader.wait(); - - EXPECT_TRUE(queue.empty()); - EXPECT_TRUE(expectedSequences.empty()); - EXPECT_EQ(totalEnqueued, kTOTAL_ITEMS_PER_THREAD * kTOTAL_THREADS); - EXPECT_GE(totalSleepCycles, 1uz); -} - -TEST(StrandedPriorityQueueTests, ReturnsNulloptIfQueueEmpty) -{ - async::CoroExecutionContext realCtx; - StrandedPriorityQueue queue{realCtx.makeStrand()}; - - EXPECT_TRUE(queue.empty()); - auto maybeValue = queue.dequeue(); - EXPECT_FALSE(maybeValue.has_value()); -} From 3e520c8742baacd9d94a5e8ee3e45b6dc6ca338e Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 26 May 2025 15:48:17 +0100 Subject: [PATCH 04/33] chore: Fix: nagetive -> negative (#2156) --- tests/unit/rpc/handlers/AccountChannelsTests.cpp | 2 +- tests/unit/rpc/handlers/AccountLinesTests.cpp | 2 +- tests/unit/rpc/handlers/AccountObjectsTests.cpp | 2 +- tests/unit/rpc/handlers/BookOffersTests.cpp | 2 +- tests/unit/rpc/handlers/LedgerDataTests.cpp | 2 +- tests/unit/rpc/handlers/NFTHistoryTests.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/rpc/handlers/AccountChannelsTests.cpp b/tests/unit/rpc/handlers/AccountChannelsTests.cpp index a2446dadb..1e60452ae 100644 --- a/tests/unit/rpc/handlers/AccountChannelsTests.cpp +++ b/tests/unit/rpc/handlers/AccountChannelsTests.cpp @@ -83,7 +83,7 @@ TEST_F(RPCAccountChannelsHandlerTest, LimitNotInt) }); } -TEST_F(RPCAccountChannelsHandlerTest, LimitNagetive) +TEST_F(RPCAccountChannelsHandlerTest, LimitNegative) { runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; diff --git a/tests/unit/rpc/handlers/AccountLinesTests.cpp b/tests/unit/rpc/handlers/AccountLinesTests.cpp index 0d6438ede..6c5414d6a 100644 --- a/tests/unit/rpc/handlers/AccountLinesTests.cpp +++ b/tests/unit/rpc/handlers/AccountLinesTests.cpp @@ -285,7 +285,7 @@ TEST_F(RPCAccountLinesHandlerTest, LimitNotInt) }); } -TEST_F(RPCAccountLinesHandlerTest, LimitNagetive) +TEST_F(RPCAccountLinesHandlerTest, LimitNegative) { runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; diff --git a/tests/unit/rpc/handlers/AccountObjectsTests.cpp b/tests/unit/rpc/handlers/AccountObjectsTests.cpp index 7acf7773c..86a057760 100644 --- a/tests/unit/rpc/handlers/AccountObjectsTests.cpp +++ b/tests/unit/rpc/handlers/AccountObjectsTests.cpp @@ -149,7 +149,7 @@ generateTestValuesForParametersTest() .expectedErrorMessage = "Invalid parameters." }, AccountObjectsParamTestCaseBundle{ - .testName = "LimitNagetive", + .testName = "LimitNegative", .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "limit":-1})", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." diff --git a/tests/unit/rpc/handlers/BookOffersTests.cpp b/tests/unit/rpc/handlers/BookOffersTests.cpp index 9e8ea9790..2f52f2d29 100644 --- a/tests/unit/rpc/handlers/BookOffersTests.cpp +++ b/tests/unit/rpc/handlers/BookOffersTests.cpp @@ -333,7 +333,7 @@ generateParameterBookOffersTestBundles() .expectedErrorMessage = "Invalid parameters." }, ParameterTestBundle{ - .testName = "LimitNagetive", + .testName = "LimitNegative", .testJson = R"({ "taker_pays" : { diff --git a/tests/unit/rpc/handlers/LedgerDataTests.cpp b/tests/unit/rpc/handlers/LedgerDataTests.cpp index 6fe3c9b5e..303f1fb64 100644 --- a/tests/unit/rpc/handlers/LedgerDataTests.cpp +++ b/tests/unit/rpc/handlers/LedgerDataTests.cpp @@ -111,7 +111,7 @@ generateTestValuesForParametersTest() .expectedErrorMessage = "Invalid parameters." }, LedgerDataParamTestCaseBundle{ - .testName = "limitNagetive", + .testName = "limitNegative", .testJson = R"({"limit": -1})", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." diff --git a/tests/unit/rpc/handlers/NFTHistoryTests.cpp b/tests/unit/rpc/handlers/NFTHistoryTests.cpp index 9841819ee..2b0d5acca 100644 --- a/tests/unit/rpc/handlers/NFTHistoryTests.cpp +++ b/tests/unit/rpc/handlers/NFTHistoryTests.cpp @@ -138,7 +138,7 @@ generateTestValuesForParametersTest() .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ - .testName = "limitNagetive", + .testName = "limitNegative", .testJson = R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": -1})", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." From 3e83b54332b0fdd576377e9b3b59491f7e4671d6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 16:56:39 +0100 Subject: [PATCH 05/33] style: clang-tidy auto fixes (#2159) Fixes #2158. Please review and commit clang-tidy fixes. Co-authored-by: godexsoft <385326+godexsoft@users.noreply.github.com> Co-authored-by: Ayaz Salikhov --- src/etl/ETLService.cpp | 4 ---- src/etlng/ETLService.cpp | 10 ---------- tests/unit/etlng/ETLServiceTests.cpp | 8 +++----- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/etl/ETLService.cpp b/src/etl/ETLService.cpp index ac1bec786..a363fc756 100644 --- a/src/etl/ETLService.cpp +++ b/src/etl/ETLService.cpp @@ -47,11 +47,7 @@ #include "util/log/Logger.hpp" #include -#include -#include -#include #include -#include #include #include diff --git a/src/etlng/ETLService.cpp b/src/etlng/ETLService.cpp index 0116f0a4c..0d0eb7e51 100644 --- a/src/etlng/ETLService.cpp +++ b/src/etlng/ETLService.cpp @@ -54,19 +54,9 @@ #include "util/async/AnyExecutionContext.hpp" #include "util/log/Logger.hpp" -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include diff --git a/tests/unit/etlng/ETLServiceTests.cpp b/tests/unit/etlng/ETLServiceTests.cpp index bcdf65f41..6023e061c 100644 --- a/tests/unit/etlng/ETLServiceTests.cpp +++ b/tests/unit/etlng/ETLServiceTests.cpp @@ -47,11 +47,9 @@ #include "util/config/ConfigValue.hpp" #include "util/config/Types.hpp" -#include #include -#include #include -#include +#include #include #include #include @@ -262,7 +260,7 @@ TEST_F(ETLServiceTests, RunWithEmptyDatabase) auto mockTaskManager = std::make_unique>(); auto ledgerData = createTestData(kSEQ); - testing::Sequence s; + testing::Sequence const s; EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)).InSequence(s).WillOnce(testing::Return(std::nullopt)); EXPECT_CALL(*ledgers_, getMostRecent()).WillRepeatedly(testing::Return(kSEQ)); EXPECT_CALL(*extractor_, extractLedgerOnly(kSEQ)).WillOnce(testing::Return(ledgerData)); @@ -326,7 +324,7 @@ TEST_F(ETLServiceAssertTests, FailToLoadInitialLedger) TEST_F(ETLServiceAssertTests, WaitForValidatedLedgerIsAbortedLeadToFailToLoadInitialLedger) { - testing::Sequence s; + testing::Sequence const s; EXPECT_CALL(*backend_, hardFetchLedgerRange(testing::_)).WillOnce(testing::Return(std::nullopt)); EXPECT_CALL(*ledgers_, getMostRecent()).InSequence(s).WillOnce(testing::Return(std::nullopt)); EXPECT_CALL(*ledgers_, getMostRecent()).InSequence(s).WillOnce(testing::Return(kSEQ)); From 28062496ebbb7c36a926922eafc269e17f9b6e27 Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Tue, 27 May 2025 13:05:03 +0100 Subject: [PATCH 06/33] feat: ETLng MPT support (#2154) --- src/etl/ETLService.cpp | 8 +- src/etlng/CMakeLists.txt | 1 + src/etlng/impl/ext/Cache.cpp | 7 +- src/etlng/impl/ext/Cache.hpp | 6 +- src/etlng/impl/ext/Core.cpp | 10 +- src/etlng/impl/ext/Core.hpp | 10 +- src/etlng/impl/ext/MPT.cpp | 81 ++++++++++++ src/etlng/impl/ext/MPT.hpp | 57 ++++++++ src/etlng/impl/ext/NFT.cpp | 9 +- src/etlng/impl/ext/NFT.hpp | 8 +- tests/common/util/BinaryTestObject.cpp | 34 ++++- tests/common/util/BinaryTestObject.hpp | 7 +- tests/unit/CMakeLists.txt | 1 + tests/unit/etlng/ExtractionTests.cpp | 4 +- tests/unit/etlng/ext/MPTTests.cpp | 173 +++++++++++++++++++++++++ 15 files changed, 380 insertions(+), 36 deletions(-) create mode 100644 src/etlng/impl/ext/MPT.cpp create mode 100644 src/etlng/impl/ext/MPT.hpp create mode 100644 tests/unit/etlng/ext/MPTTests.cpp diff --git a/src/etl/ETLService.cpp b/src/etl/ETLService.cpp index a363fc756..63ea45914 100644 --- a/src/etl/ETLService.cpp +++ b/src/etl/ETLService.cpp @@ -39,6 +39,11 @@ #include "etlng/LoadBalancerInterface.hpp" #include "etlng/impl/LedgerPublisher.hpp" #include "etlng/impl/TaskManagerProvider.hpp" +#include "etlng/impl/ext/Cache.hpp" +#include "etlng/impl/ext/Core.hpp" +#include "etlng/impl/ext/MPT.hpp" +#include "etlng/impl/ext/NFT.hpp" +#include "etlng/impl/ext/Successor.hpp" #include "feed/SubscriptionManagerInterface.hpp" #include "util/Assert.hpp" #include "util/Constants.hpp" @@ -96,7 +101,8 @@ ETLService::makeETLService( etlng::impl::CacheExt{cacheUpdater}, etlng::impl::CoreExt{backend}, etlng::impl::SuccessorExt{backend, backend->cache()}, - etlng::impl::NFTExt{backend} + etlng::impl::NFTExt{backend}, + etlng::impl::MPTExt{backend} ), amendmentBlockHandler ); diff --git a/src/etlng/CMakeLists.txt b/src/etlng/CMakeLists.txt index d0640b545..bb0d6a6f0 100644 --- a/src/etlng/CMakeLists.txt +++ b/src/etlng/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources( impl/TaskManager.cpp impl/ext/Cache.cpp impl/ext/Core.cpp + impl/ext/MPT.cpp impl/ext/NFT.cpp impl/ext/Successor.cpp ) diff --git a/src/etlng/impl/ext/Cache.cpp b/src/etlng/impl/ext/Cache.cpp index 221f639ac..c34b86b9c 100644 --- a/src/etlng/impl/ext/Cache.cpp +++ b/src/etlng/impl/ext/Cache.cpp @@ -36,14 +36,14 @@ CacheExt::CacheExt(std::shared_ptr cacheUpdater) : cacheU } void -CacheExt::onLedgerData(model::LedgerData const& data) const +CacheExt::onLedgerData(model::LedgerData const& data) { - cacheUpdater_->update(data); LOG(log_.trace()) << "got data. objects cnt = " << data.objects.size(); + cacheUpdater_->update(data); } void -CacheExt::onInitialData(model::LedgerData const& data) const +CacheExt::onInitialData(model::LedgerData const& data) { LOG(log_.trace()) << "got initial data. objects cnt = " << data.objects.size(); cacheUpdater_->update(data); @@ -52,7 +52,6 @@ CacheExt::onInitialData(model::LedgerData const& data) const void CacheExt::onInitialObjects(uint32_t seq, std::vector const& objs, [[maybe_unused]] std::string lastKey) - const { LOG(log_.trace()) << "got initial objects cnt = " << objs.size(); cacheUpdater_->update(seq, objs); diff --git a/src/etlng/impl/ext/Cache.hpp b/src/etlng/impl/ext/Cache.hpp index 51bf5cd56..c8a050e16 100644 --- a/src/etlng/impl/ext/Cache.hpp +++ b/src/etlng/impl/ext/Cache.hpp @@ -40,13 +40,13 @@ public: CacheExt(std::shared_ptr cacheUpdater); void - onLedgerData(model::LedgerData const& data) const; + onLedgerData(model::LedgerData const& data); void - onInitialData(model::LedgerData const& data) const; + onInitialData(model::LedgerData const& data); void - onInitialObjects(uint32_t seq, std::vector const& objs, [[maybe_unused]] std::string lastKey) const; + onInitialObjects(uint32_t seq, std::vector const& objs, [[maybe_unused]] std::string lastKey); // We want cache updates through ETL if we are a potential writer but currently are not writing to DB static bool diff --git a/src/etlng/impl/ext/Core.cpp b/src/etlng/impl/ext/Core.cpp index 3049961fd..c10016707 100644 --- a/src/etlng/impl/ext/Core.cpp +++ b/src/etlng/impl/ext/Core.cpp @@ -34,7 +34,7 @@ CoreExt::CoreExt(std::shared_ptr backend) : backend_(std::move } void -CoreExt::onLedgerData(model::LedgerData const& data) const +CoreExt::onLedgerData(model::LedgerData const& data) { LOG(log_.debug()) << "Loading ledger data for " << data.seq; backend_->writeLedger(data.header, auto{data.rawHeader}); @@ -42,7 +42,7 @@ CoreExt::onLedgerData(model::LedgerData const& data) const } void -CoreExt::onInitialData(model::LedgerData const& data) const +CoreExt::onInitialData(model::LedgerData const& data) { LOG(log_.info()) << "Loading initial ledger data for " << data.seq; backend_->writeLedger(data.header, auto{data.rawHeader}); @@ -50,21 +50,21 @@ CoreExt::onInitialData(model::LedgerData const& data) const } void -CoreExt::onInitialObject(uint32_t seq, model::Object const& obj) const +CoreExt::onInitialObject(uint32_t seq, model::Object const& obj) { LOG(log_.trace()) << "got initial OBJ = " << obj.key << " for seq " << seq; backend_->writeLedgerObject(auto{obj.keyRaw}, seq, auto{obj.dataRaw}); } void -CoreExt::onObject(uint32_t seq, model::Object const& obj) const +CoreExt::onObject(uint32_t seq, model::Object const& obj) { LOG(log_.trace()) << "got OBJ = " << obj.key << " for seq " << seq; backend_->writeLedgerObject(auto{obj.keyRaw}, seq, auto{obj.dataRaw}); } void -CoreExt::insertTransactions(model::LedgerData const& data) const +CoreExt::insertTransactions(model::LedgerData const& data) { for (auto const& txn : data.transactions) { LOG(log_.trace()) << "Inserting transaction = " << txn.sttx.getTransactionID(); diff --git a/src/etlng/impl/ext/Core.hpp b/src/etlng/impl/ext/Core.hpp index 7913c15ef..cafd8f5ce 100644 --- a/src/etlng/impl/ext/Core.hpp +++ b/src/etlng/impl/ext/Core.hpp @@ -39,20 +39,20 @@ public: CoreExt(std::shared_ptr backend); void - onLedgerData(model::LedgerData const& data) const; + onLedgerData(model::LedgerData const& data); void - onInitialData(model::LedgerData const& data) const; + onInitialData(model::LedgerData const& data); void - onInitialObject(uint32_t seq, model::Object const& obj) const; + onInitialObject(uint32_t seq, model::Object const& obj); void - onObject(uint32_t seq, model::Object const& obj) const; + onObject(uint32_t seq, model::Object const& obj); private: void - insertTransactions(model::LedgerData const& data) const; + insertTransactions(model::LedgerData const& data); }; } // namespace etlng::impl diff --git a/src/etlng/impl/ext/MPT.cpp b/src/etlng/impl/ext/MPT.cpp new file mode 100644 index 000000000..a2e0f33c6 --- /dev/null +++ b/src/etlng/impl/ext/MPT.cpp @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include "etlng/impl/ext/MPT.hpp" + +#include "data/BackendInterface.hpp" +#include "data/DBHelpers.hpp" +#include "etl/MPTHelpers.hpp" +#include "etlng/Models.hpp" +#include "util/log/Logger.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace etlng::impl { + +MPTExt::MPTExt(std::shared_ptr backend) : backend_(std::move(backend)) +{ +} + +void +MPTExt::onLedgerData(model::LedgerData const& data) +{ + LOG(log_.trace()) << "got TXS cnt = " << data.transactions.size() << "; OBJS size = " << data.objects.size(); + writeMPTHoldersFromTransactions(data); +} + +void +MPTExt::onInitialObject(uint32_t, model::Object const& obj) +{ + LOG(log_.trace()) << "got initial object with key: " << ripple::strHex(obj.key); + if (auto const mptHolder = etl::getMPTHolderFromObj(obj.keyRaw, obj.dataRaw); mptHolder.has_value()) + backend_->writeMPTHolders({*mptHolder}); +} + +void +MPTExt::onInitialData(model::LedgerData const& data) +{ + LOG(log_.trace()) << "got initial TXS cnt = " << data.transactions.size(); + writeMPTHoldersFromTransactions(data); +} + +void +MPTExt::writeMPTHoldersFromTransactions(model::LedgerData const& data) +{ + std::vector holders; + + for (auto const& tx : data.transactions) { + if (auto const mptHolder = etl::getMPTHolderFromTx(tx.meta, tx.sttx); mptHolder.has_value()) + holders.push_back(*mptHolder); + } + + if (not holders.empty()) + backend_->writeMPTHolders(holders); +} + +} // namespace etlng::impl diff --git a/src/etlng/impl/ext/MPT.hpp b/src/etlng/impl/ext/MPT.hpp new file mode 100644 index 000000000..e883dc5dd --- /dev/null +++ b/src/etlng/impl/ext/MPT.hpp @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include "data/BackendInterface.hpp" +#include "etlng/Models.hpp" +#include "util/log/Logger.hpp" + +#include +#include +#include +#include + +#include +#include + +namespace etlng::impl { + +class MPTExt { + std::shared_ptr backend_; + util::Logger log_{"ETL"}; + +public: + explicit MPTExt(std::shared_ptr backend); + + void + onLedgerData(model::LedgerData const& data); + + void + onInitialObject(uint32_t seq, model::Object const& obj); + + void + onInitialData(model::LedgerData const& data); + +private: + void + writeMPTHoldersFromTransactions(model::LedgerData const& data); +}; + +} // namespace etlng::impl diff --git a/src/etlng/impl/ext/NFT.cpp b/src/etlng/impl/ext/NFT.cpp index 30566acf9..a5b9575e4 100644 --- a/src/etlng/impl/ext/NFT.cpp +++ b/src/etlng/impl/ext/NFT.cpp @@ -37,27 +37,28 @@ NFTExt::NFTExt(std::shared_ptr backend) : backend_(std::move(b } void -NFTExt::onLedgerData(model::LedgerData const& data) const +NFTExt::onLedgerData(model::LedgerData const& data) { + LOG(log_.trace()) << "got TXS cnt = " << data.transactions.size() << "; OBJS size = " << data.objects.size(); writeNFTs(data); } void -NFTExt::onInitialObject(uint32_t seq, model::Object const& obj) const +NFTExt::onInitialObject(uint32_t seq, model::Object const& obj) { LOG(log_.trace()) << "got initial object with key = " << obj.key; backend_->writeNFTs(etl::getNFTDataFromObj(seq, obj.keyRaw, obj.dataRaw)); } void -NFTExt::onInitialData(model::LedgerData const& data) const +NFTExt::onInitialData(model::LedgerData const& data) { LOG(log_.trace()) << "got initial TXS cnt = " << data.transactions.size(); writeNFTs(data); } void -NFTExt::writeNFTs(model::LedgerData const& data) const +NFTExt::writeNFTs(model::LedgerData const& data) { std::vector nfts; std::vector nftTxs; diff --git a/src/etlng/impl/ext/NFT.hpp b/src/etlng/impl/ext/NFT.hpp index 6d6a146cf..c7915202f 100644 --- a/src/etlng/impl/ext/NFT.hpp +++ b/src/etlng/impl/ext/NFT.hpp @@ -36,17 +36,17 @@ public: NFTExt(std::shared_ptr backend); void - onLedgerData(model::LedgerData const& data) const; + onLedgerData(model::LedgerData const& data); void - onInitialObject(uint32_t seq, model::Object const& obj) const; + onInitialObject(uint32_t seq, model::Object const& obj); void - onInitialData(model::LedgerData const& data) const; + onInitialData(model::LedgerData const& data); private: void - writeNFTs(model::LedgerData const& data) const; + writeNFTs(model::LedgerData const& data); }; } // namespace etlng::impl diff --git a/tests/common/util/BinaryTestObject.cpp b/tests/common/util/BinaryTestObject.cpp index 30cd07a2c..70f4fe2ad 100644 --- a/tests/common/util/BinaryTestObject.cpp +++ b/tests/common/util/BinaryTestObject.cpp @@ -56,18 +56,18 @@ constinit auto const kRAW_HEADER = namespace util { std::pair -createNftTxAndMetaBlobs(std::string metaStr, std::string txnStr) +createTxAndMetaBlobs(std::string metaStr, std::string txnStr) { return {hexStringToBinaryString(metaStr), hexStringToBinaryString(txnStr)}; } std::pair -createNftTxAndMeta(std::string hashStr, std::string metaStr, std::string txnStr) +createTxAndMeta(std::string hashStr, std::string metaStr, std::string txnStr) { ripple::uint256 hash; EXPECT_TRUE(hash.parseHex(hashStr)); - auto const [metaBlob, txnBlob] = createNftTxAndMetaBlobs(metaStr, txnStr); + auto const [metaBlob, txnBlob] = createTxAndMetaBlobs(metaStr, txnStr); ripple::SerialIter it{txnBlob.data(), txnBlob.size()}; return {ripple::STTx{it}, ripple::TxMeta{hash, kSEQ, metaBlob}}; @@ -76,7 +76,7 @@ createNftTxAndMeta(std::string hashStr, std::string metaStr, std::string txnStr) etlng::model::Transaction createTransaction(ripple::TxType type, std::string hashStr, std::string metaStr, std::string txnStr) { - auto const [sttx, meta] = createNftTxAndMeta(hashStr, metaStr, txnStr); + auto const [sttx, meta] = createTxAndMeta(hashStr, metaStr, txnStr); return { .raw = "", .metaRaw = "", @@ -161,6 +161,28 @@ createObjectWithTwoNFTs() }; } +etlng::model::Object +createObjectWithMPT() +{ + constexpr auto kACCOUNT = "rM2AGCCCRb373FRuD8wHyUwUsh2dV4BW5Q"; + + auto const account = getAccountIdWithString(kACCOUNT); + auto const mptokenObject = createMpTokenObject(kACCOUNT, ripple::makeMptID(2, getAccountIdWithString(kACCOUNT))); + + return { + .key = {}, + .keyRaw = std::string(reinterpret_cast(account.data()), ripple::AccountID::size()), + .data = {}, + .dataRaw = std::string( + static_cast(mptokenObject.getSerializer().getDataPtr()), + mptokenObject.getSerializer().getDataLength() + ), + .successor = "", + .predecessor = "", + .type = etlng::model::Object::ModType::Created, + }; +} + etlng::model::BookSuccessor createSuccessor() { @@ -184,7 +206,7 @@ createDataAndDiff() { auto original = org::xrpl::rpc::v1::TransactionAndMetadata(); - auto const [metaRaw, txRaw] = createNftTxAndMetaBlobs(); + auto const [metaRaw, txRaw] = createTxAndMetaBlobs(); original.set_transaction_blob(txRaw); original.set_metadata_blob(metaRaw); for (int i = 0; i < 10; ++i) { @@ -230,7 +252,7 @@ createData() { auto original = org::xrpl::rpc::v1::TransactionAndMetadata(); - auto const [metaRaw, txRaw] = createNftTxAndMetaBlobs(); + auto const [metaRaw, txRaw] = createTxAndMetaBlobs(); original.set_transaction_blob(txRaw); original.set_metadata_blob(metaRaw); for (int i = 0; i < 10; ++i) { diff --git a/tests/common/util/BinaryTestObject.hpp b/tests/common/util/BinaryTestObject.hpp index 41d5029ef..aadbf5895 100644 --- a/tests/common/util/BinaryTestObject.hpp +++ b/tests/common/util/BinaryTestObject.hpp @@ -144,10 +144,10 @@ static constexpr auto kDEFAULT_HASH = "6C7F69A6D25A13AC4A2E9145999F45D4674F93990 static constexpr auto kDEFAULT_OBJ_KEY = "B00AA769C00726371689ED66A7CF57C2502F1BF4BDFF2ACADF67A2A7B5E8960D"; [[maybe_unused, nodiscard]] std::pair -createNftTxAndMetaBlobs(std::string metaStr = kDEFAULT_TXN_META, std::string txnStr = kDEFAULT_TXN_HEX); +createTxAndMetaBlobs(std::string metaStr = kDEFAULT_TXN_META, std::string txnStr = kDEFAULT_TXN_HEX); [[maybe_unused, nodiscard]] std::pair -createNftTxAndMeta( +createTxAndMeta( std::string hashStr = kDEFAULT_HASH, std::string metaStr = kDEFAULT_TXN_META, std::string txnStr = kDEFAULT_TXN_HEX @@ -176,6 +176,9 @@ createObjectWithBookBase( [[maybe_unused, nodiscard]] etlng::model::Object createObjectWithTwoNFTs(); +[[maybe_unused, nodiscard]] etlng::model::Object +createObjectWithMPT(); + [[maybe_unused, nodiscard]] etlng::model::BookSuccessor createSuccessor(); diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 33b2ffc01..07a8fcc4e 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -55,6 +55,7 @@ target_sources( etlng/SourceImplTests.cpp etlng/ext/CoreTests.cpp etlng/ext/CacheTests.cpp + etlng/ext/MPTTests.cpp etlng/ext/NFTTests.cpp etlng/ext/SuccessorTests.cpp # Feed diff --git a/tests/unit/etlng/ExtractionTests.cpp b/tests/unit/etlng/ExtractionTests.cpp index 3f983bff6..d3a48cecc 100644 --- a/tests/unit/etlng/ExtractionTests.cpp +++ b/tests/unit/etlng/ExtractionTests.cpp @@ -198,7 +198,7 @@ TEST_F(ExtractionNgTests, OneTransaction) auto expected = util::createTransaction(ripple::TxType::ttNFTOKEN_CREATE_OFFER); auto original = org::xrpl::rpc::v1::TransactionAndMetadata(); - auto [metaRaw, txRaw] = util::createNftTxAndMetaBlobs(); + auto [metaRaw, txRaw] = util::createTxAndMetaBlobs(); original.set_transaction_blob(txRaw); original.set_metadata_blob(metaRaw); @@ -216,7 +216,7 @@ TEST_F(ExtractionNgTests, MultipleTransactions) auto expected = util::createTransaction(ripple::TxType::ttNFTOKEN_CREATE_OFFER); auto original = org::xrpl::rpc::v1::TransactionAndMetadata(); - auto [metaRaw, txRaw] = util::createNftTxAndMetaBlobs(); + auto [metaRaw, txRaw] = util::createTxAndMetaBlobs(); original.set_transaction_blob(txRaw); original.set_metadata_blob(metaRaw); diff --git a/tests/unit/etlng/ext/MPTTests.cpp b/tests/unit/etlng/ext/MPTTests.cpp new file mode 100644 index 000000000..91efa7a65 --- /dev/null +++ b/tests/unit/etlng/ext/MPTTests.cpp @@ -0,0 +1,173 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include "etlng/Models.hpp" +#include "etlng/impl/ext/MPT.hpp" +#include "rpc/RPCHelpers.hpp" +#include "util/BinaryTestObject.hpp" +#include "util/MockBackendTestFixture.hpp" +#include "util/MockPrometheus.hpp" +#include "util/TestObject.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace etlng; +using namespace etlng::impl; +using namespace data; +using namespace testing; + +namespace { + +constinit auto const kSEQ = 123u; +constinit auto const kLEDGER_HASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652"; +constinit auto const kHOLDER_ACCOUNT = "rK1EX542EgA9m948JrJRaEzwLVEhqWvnr9"; + +constinit auto const kTXN_HEX = + "120039220000000024002DBD1A201B002DBDA36840000000000000017321EDECF25C029811CAD07AFD616EB75E3803E44D0D59A6826AC25FE3" + "4A43626D2D157440244262E760314164843026CE2F100D0BFEB0DD6F75026FEB3F75FCAA943F5C874FF0411BC82A85DE504B434B5EC3C6A692" + "3CC37A1C2ABD3E98EFFC8240B9D0018114CEF330DB51154D8DEE249CC3D6DFD04B91F648EE0115002DBD1817E0AF9FDE4F9978B8FCD8A50636" + "30B5737DA605"; + +constinit auto const kTXN_META = + "201C00000002F8E311007F562668E165750018E0AE5808C131BAF4C26441D2BCF76F8628774DFDF098B7250BE88114CEF330DB51154D8DEE24" + "9CC3D6DFD04B91F648EE0115002DBD1817E0AF9FDE4F9978B8FCD8A5063630B5737DA605E1E1E511006425002DBD2F55E85C182A243C7CBF0E" + "F7B8B3E0C8AE68E3DE6616DE1EFE168CD913CA6520444D568F18252475DFAC9D5DE5423DFA08842F398F346DEB2BD546C526D26BF81E345CE7" + "2200000000588F18252475DFAC9D5DE5423DFA08842F398F346DEB2BD546C526D26BF81E345C8214CEF330DB51154D8DEE249CC3D6DFD04B91" + "F648EEE1E1E511006125002DBD2F55E85C182A243C7CBF0EF7B8B3E0C8AE68E3DE6616DE1EFE168CD913CA6520444D56F7D3073515F1C71F2A" + "D00941BA714A3FBE3D91AEAFCD6345B5389004AD707E95E624002DBD1A2D00000001624000000005F5E0FFE1E7220000000024002DBD1B2D00" + "000002624000000005F5E0FE8114CEF330DB51154D8DEE249CC3D6DFD04B91F648EEE1E1F1031000"; + +constinit auto const kHASH = "6005B465CBBF7FA8E41AC0C0CD38491026D9411FCB7BA46E2AEBB3AF7654261B"; +constinit auto const kHASH2 = "6005B465CBBF7FA8E41AC0C0CD38491026D9411FCB7BA46E2AEBB3AF7654261C"; +constinit auto const kHASH3 = "6005B465CBBF7FA8E41AC0C0CD38491026D9411FCB7BA46E2AEBB3AF7654261D"; + +auto +createTestData() +{ + auto transactions = std::vector{ + util::createTransaction(ripple::TxType::ttMPTOKEN_ISSUANCE_CREATE), // not AUTHORIZE so will not be written + util::createTransaction(ripple::TxType::ttMPTOKEN_AUTHORIZE, kHASH, kTXN_META, kTXN_HEX), + util::createTransaction(ripple::TxType::ttAMM_CREATE), // not MPT - will be filtered + util::createTransaction(ripple::TxType::ttMPTOKEN_ISSUANCE_CREATE), // not unique - will be filtered + }; + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + return etlng::model::LedgerData{ + .transactions = std::move(transactions), + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }; +} + +auto +createMultipleHoldersTestData() +{ + auto transactions = std::vector{ + util::createTransaction(ripple::TxType::ttMPTOKEN_AUTHORIZE, kHASH, kTXN_META, kTXN_HEX), + util::createTransaction(ripple::TxType::ttMPTOKEN_AUTHORIZE, kHASH2, kTXN_META, kTXN_HEX), + util::createTransaction(ripple::TxType::ttMPTOKEN_AUTHORIZE, kHASH3, kTXN_META, kTXN_HEX) + }; + + auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ); + return etlng::model::LedgerData{ + .transactions = std::move(transactions), + .objects = {}, + .successors = {}, + .edgeKeys = {}, + .header = header, + .rawHeader = {}, + .seq = kSEQ + }; +} + +} // namespace + +struct MPTExtTests : util::prometheus::WithPrometheus, MockBackendTest { +protected: + MPTExt ext_{backend_}; +}; + +TEST_F(MPTExtTests, OnLedgerDataFiltersAndWritesMPTs) +{ + auto const data = createTestData(); + + EXPECT_CALL(*backend_, writeMPTHolders).WillOnce([](auto const& holders) { + EXPECT_EQ(holders.size(), 1); // Only AUTHORIZE is written in the end + }); + + ext_.onLedgerData(data); +} + +TEST_F(MPTExtTests, OnInitialDataFiltersAndWritesMPTs) +{ + auto const data = createTestData(); + + EXPECT_CALL(*backend_, writeMPTHolders).WillOnce([](auto const& holders) { + EXPECT_EQ(holders.size(), 1); // Only AUTHORIZE is written in the end + }); + + ext_.onInitialData(data); +} + +TEST_F(MPTExtTests, OnInitialObjectWritesMPT) +{ + auto const data = util::createObjectWithMPT(); + + EXPECT_CALL(*backend_, writeMPTHolders).WillOnce([](auto const& holders) { EXPECT_EQ(holders.size(), 1); }); + + ext_.onInitialObject(kSEQ, data); +} + +TEST_F(MPTExtTests, OnInitialDataWithMultipleHolders) +{ + auto const data = createMultipleHoldersTestData(); + + EXPECT_CALL(*backend_, writeMPTHolders).WillOnce([](auto const& holders) { + EXPECT_EQ(holders.size(), 3); // Expect all three AUTHORIZE transactions + + auto const expectedAccount = rpc::accountFromStringStrict(kHOLDER_ACCOUNT); // Expect all three to be the same + EXPECT_TRUE(std::ranges::all_of(holders, [&expectedAccount](auto const& data) { + return data.holder == expectedAccount; + })); + }); + + ext_.onInitialData(data); +} From 561eae1b7fb9537afa0c327cd99d28969609be6c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 11:13:01 +0100 Subject: [PATCH 07/33] style: clang-tidy auto fixes (#2164) --- src/etlng/impl/ext/MPT.cpp | 3 --- tests/common/util/BinaryTestObject.cpp | 1 + tests/unit/etlng/ext/MPTTests.cpp | 12 ------------ 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/etlng/impl/ext/MPT.cpp b/src/etlng/impl/ext/MPT.cpp index a2e0f33c6..ea0816e57 100644 --- a/src/etlng/impl/ext/MPT.cpp +++ b/src/etlng/impl/ext/MPT.cpp @@ -26,9 +26,6 @@ #include "util/log/Logger.hpp" #include -#include -#include -#include #include #include diff --git a/tests/common/util/BinaryTestObject.cpp b/tests/common/util/BinaryTestObject.cpp index 70f4fe2ad..f4d0df25b 100644 --- a/tests/common/util/BinaryTestObject.cpp +++ b/tests/common/util/BinaryTestObject.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/unit/etlng/ext/MPTTests.cpp b/tests/unit/etlng/ext/MPTTests.cpp index 91efa7a65..4a8c5f0b2 100644 --- a/tests/unit/etlng/ext/MPTTests.cpp +++ b/tests/unit/etlng/ext/MPTTests.cpp @@ -27,19 +27,7 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include #include From 09409fc05d9adb70975fb32092554c367f4e4b91 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 28 May 2025 12:58:37 +0100 Subject: [PATCH 08/33] ci: Add missing workflow dependencies (#2155) Was discovered in https://github.com/XRPLF/clio/pull/2150, better to be fixed separately, as I'm not sure it'll be merged. --- .github/workflows/build.yml | 1 + .github/workflows/nightly.yml | 1 + .github/workflows/sanitizers.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a26139996..fa94b84e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ on: - "!.github/actions/create_issue/**" - CMakeLists.txt + - conanfile.py - "cmake/**" - "src/**" - "tests/**" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 387b83524..e04ac8075 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -11,6 +11,7 @@ on: - .github/workflows/release_impl.yml - .github/workflows/build_and_test.yml - .github/workflows/build_impl.yml + - .github/workflows/test_impl.yml - .github/workflows/build_clio_docker_image.yml - ".github/actions/**" diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 2b7116dca..6088be3c7 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -18,6 +18,7 @@ on: - .github/scripts/execute-tests-under-sanitizer - CMakeLists.txt + - conanfile.py - "cmake/**" # We don't run sanitizer on code change, because it takes too long # - "src/**" From 9b69da7f91965f9a063510146b76c627daf6a5e1 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 28 May 2025 14:03:38 +0100 Subject: [PATCH 09/33] test: Skip slow DB sleep-based test on Mac (#2148) Fix: https://github.com/XRPLF/clio/issues/2147 Fix: https://github.com/XRPLF/clio/issues/2132 --- tests/integration/data/cassandra/BackendTests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/data/cassandra/BackendTests.cpp b/tests/integration/data/cassandra/BackendTests.cpp index 56db6a4e0..c69d82e83 100644 --- a/tests/integration/data/cassandra/BackendTests.cpp +++ b/tests/integration/data/cassandra/BackendTests.cpp @@ -1421,6 +1421,9 @@ TEST_F(BackendCassandraNodeMessageTest, MessageDisappearsAfterTTL) TEST_F(BackendCassandraNodeMessageTest, UpdatingMessageKeepsItAlive) { +#if defined(__APPLE__) + GTEST_SKIP() << "Skipping test on Apple platform due to slow DB"; +#endif static boost::uuids::uuid const kUUID = generateUuid(); static std::string const kUPDATED_MESSAGE = "updated message"; From 57b8ff1c4958658ac814663276e16ecfb0bfb703 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 29 May 2025 19:59:40 +0100 Subject: [PATCH 10/33] fix: Use UniformRandomGenerator class to prevent threading issue (#2165) --- .../util/async/ExecutionContextBenchmarks.cpp | 3 +- src/app/ClioApplication.cpp | 8 ++- src/etl/LoadBalancer.cpp | 17 ++++-- src/etl/LoadBalancer.hpp | 7 +++ src/etlng/LoadBalancer.cpp | 17 ++++-- src/etlng/LoadBalancer.hpp | 7 +++ src/util/Random.cpp | 16 +++-- src/util/Random.hpp | 61 ++++++++++++++++--- tests/common/util/MockRandomGenerator.hpp | 31 ++++++++++ .../data/cassandra/BackendTests.cpp | 3 +- tests/unit/etl/LoadBalancerTests.cpp | 16 ++--- tests/unit/etlng/LoadBalancerTests.cpp | 17 +++--- tests/unit/util/RandomTests.cpp | 19 +++--- 13 files changed, 174 insertions(+), 48 deletions(-) create mode 100644 tests/common/util/MockRandomGenerator.hpp diff --git a/benchmarks/util/async/ExecutionContextBenchmarks.cpp b/benchmarks/util/async/ExecutionContextBenchmarks.cpp index fead09464..c864e5d43 100644 --- a/benchmarks/util/async/ExecutionContextBenchmarks.cpp +++ b/benchmarks/util/async/ExecutionContextBenchmarks.cpp @@ -191,8 +191,9 @@ generateData() constexpr auto kTOTAL = 10'000; std::vector data; data.reserve(kTOTAL); + util::MTRandomGenerator randomGenerator; for (auto i = 0; i < kTOTAL; ++i) - data.push_back(util::Random::uniform(1, 100'000'000)); + data.push_back(randomGenerator.uniform(1, 100'000'000)); return data; } diff --git a/src/app/ClioApplication.cpp b/src/app/ClioApplication.cpp index 903089d6b..61611d1b0 100644 --- a/src/app/ClioApplication.cpp +++ b/src/app/ClioApplication.cpp @@ -145,9 +145,13 @@ ClioApplication::run(bool const useNgWebServer) // The balancer itself publishes to streams (transactions_proposed and accounts_proposed) auto balancer = [&] -> std::shared_ptr { if (config_.get("__ng_etl")) - return etlng::LoadBalancer::makeLoadBalancer(config_, ioc, backend, subscriptions, ledgers); + return etlng::LoadBalancer::makeLoadBalancer( + config_, ioc, backend, subscriptions, std::make_unique(), ledgers + ); - return etl::LoadBalancer::makeLoadBalancer(config_, ioc, backend, subscriptions, ledgers); + return etl::LoadBalancer::makeLoadBalancer( + config_, ioc, backend, subscriptions, std::make_unique(), ledgers + ); }(); // ETL is responsible for writing and publishing to streams. In read-only mode, ETL only publishes diff --git a/src/etl/LoadBalancer.cpp b/src/etl/LoadBalancer.cpp index 12b8877ff..394e4af78 100644 --- a/src/etl/LoadBalancer.cpp +++ b/src/etl/LoadBalancer.cpp @@ -71,12 +71,19 @@ LoadBalancer::makeLoadBalancer( boost::asio::io_context& ioc, std::shared_ptr backend, std::shared_ptr subscriptions, + std::unique_ptr randomGenerator, std::shared_ptr validatedLedgers, SourceFactory sourceFactory ) { return std::make_shared( - config, ioc, std::move(backend), std::move(subscriptions), std::move(validatedLedgers), std::move(sourceFactory) + config, + ioc, + std::move(backend), + std::move(subscriptions), + std::move(randomGenerator), + std::move(validatedLedgers), + std::move(sourceFactory) ); } @@ -85,10 +92,12 @@ LoadBalancer::LoadBalancer( boost::asio::io_context& ioc, std::shared_ptr backend, std::shared_ptr subscriptions, + std::unique_ptr randomGenerator, std::shared_ptr validatedLedgers, SourceFactory sourceFactory ) - : forwardingCounters_{ + : randomGenerator_(std::move(randomGenerator)) + , forwardingCounters_{ .successDuration = PrometheusService::counterInt( "forwarding_duration_milliseconds_counter", Labels({util::prometheus::Label{"status", "success"}}), @@ -319,7 +328,7 @@ void LoadBalancer::execute(Func f, uint32_t ledgerSequence, std::chrono::steady_clock::duration retryAfter) { ASSERT(not sources_.empty(), "ETL sources must be configured to execute functions."); - size_t sourceIdx = util::Random::uniform(0ul, sources_.size() - 1); + size_t sourceIdx = randomGenerator_->uniform(0ul, sources_.size() - 1); size_t numAttempts = 0; @@ -403,7 +412,7 @@ LoadBalancer::forwardToRippledImpl( ++forwardingCounters_.cacheMiss.get(); ASSERT(not sources_.empty(), "ETL sources must be configured to forward requests."); - std::size_t sourceIdx = util::Random::uniform(0ul, sources_.size() - 1); + std::size_t sourceIdx = randomGenerator_->uniform(0ul, sources_.size() - 1); auto numAttempts = 0u; diff --git a/src/etl/LoadBalancer.hpp b/src/etl/LoadBalancer.hpp index 8edb74788..b6eba6802 100644 --- a/src/etl/LoadBalancer.hpp +++ b/src/etl/LoadBalancer.hpp @@ -29,6 +29,7 @@ #include "rpc/Errors.hpp" #include "util/Assert.hpp" #include "util/Mutex.hpp" +#include "util/Random.hpp" #include "util/ResponseExpirationCache.hpp" #include "util/config/ConfigDefinition.hpp" #include "util/log/Logger.hpp" @@ -88,6 +89,8 @@ private: std::optional forwardingCache_; std::optional forwardingXUserValue_; + std::unique_ptr randomGenerator_; + std::vector sources_; std::optional etlState_; std::uint32_t downloadRanges_ = @@ -123,6 +126,7 @@ public: * @param ioc The io_context to run on * @param backend BackendInterface implementation * @param subscriptions Subscription manager + * @param randomGenerator A random generator to use for selecting sources * @param validatedLedgers The network validated ledgers datastructure * @param sourceFactory A factory function to create a source */ @@ -131,6 +135,7 @@ public: boost::asio::io_context& ioc, std::shared_ptr backend, std::shared_ptr subscriptions, + std::unique_ptr randomGenerator, std::shared_ptr validatedLedgers, SourceFactory sourceFactory = makeSource ); @@ -142,6 +147,7 @@ public: * @param ioc The io_context to run on * @param backend BackendInterface implementation * @param subscriptions Subscription manager + * @param randomGenerator A random generator to use for selecting sources * @param validatedLedgers The network validated ledgers data structure * @param sourceFactory A factory function to create a source * @return A shared pointer to a new instance of LoadBalancer @@ -152,6 +158,7 @@ public: boost::asio::io_context& ioc, std::shared_ptr backend, std::shared_ptr subscriptions, + std::unique_ptr randomGenerator, std::shared_ptr validatedLedgers, SourceFactory sourceFactory = makeSource ); diff --git a/src/etlng/LoadBalancer.cpp b/src/etlng/LoadBalancer.cpp index 1dc528e61..ff66bb7a4 100644 --- a/src/etlng/LoadBalancer.cpp +++ b/src/etlng/LoadBalancer.cpp @@ -72,12 +72,19 @@ LoadBalancer::makeLoadBalancer( boost::asio::io_context& ioc, std::shared_ptr backend, std::shared_ptr subscriptions, + std::unique_ptr randomGenerator, std::shared_ptr validatedLedgers, SourceFactory sourceFactory ) { return std::make_shared( - config, ioc, std::move(backend), std::move(subscriptions), std::move(validatedLedgers), std::move(sourceFactory) + config, + ioc, + std::move(backend), + std::move(subscriptions), + std::move(randomGenerator), + std::move(validatedLedgers), + std::move(sourceFactory) ); } @@ -86,10 +93,12 @@ LoadBalancer::LoadBalancer( boost::asio::io_context& ioc, std::shared_ptr backend, std::shared_ptr subscriptions, + std::unique_ptr randomGenerator, std::shared_ptr validatedLedgers, SourceFactory sourceFactory ) - : forwardingCounters_{ + : randomGenerator_(std::move(randomGenerator)) + , forwardingCounters_{ .successDuration = PrometheusService::counterInt( "forwarding_duration_milliseconds_counter", Labels({util::prometheus::Label{"status", "success"}}), @@ -323,7 +332,7 @@ void LoadBalancer::execute(Func f, uint32_t ledgerSequence, std::chrono::steady_clock::duration retryAfter) { ASSERT(not sources_.empty(), "ETL sources must be configured to execute functions."); - size_t sourceIdx = util::Random::uniform(0ul, sources_.size() - 1); + size_t sourceIdx = randomGenerator_->uniform(0ul, sources_.size() - 1); size_t numAttempts = 0; @@ -407,7 +416,7 @@ LoadBalancer::forwardToRippledImpl( ++forwardingCounters_.cacheMiss.get(); ASSERT(not sources_.empty(), "ETL sources must be configured to forward requests."); - std::size_t sourceIdx = util::Random::uniform(0ul, sources_.size() - 1); + std::size_t sourceIdx = randomGenerator_->uniform(0ul, sources_.size() - 1); auto numAttempts = 0u; diff --git a/src/etlng/LoadBalancer.hpp b/src/etlng/LoadBalancer.hpp index 3c6fd6032..10c48e2ff 100644 --- a/src/etlng/LoadBalancer.hpp +++ b/src/etlng/LoadBalancer.hpp @@ -29,6 +29,7 @@ #include "rpc/Errors.hpp" #include "util/Assert.hpp" #include "util/Mutex.hpp" +#include "util/Random.hpp" #include "util/ResponseExpirationCache.hpp" #include "util/config/ConfigDefinition.hpp" #include "util/log/Logger.hpp" @@ -88,6 +89,8 @@ private: std::optional forwardingCache_; std::optional forwardingXUserValue_; + std::unique_ptr randomGenerator_; + std::vector sources_; std::optional etlState_; std::uint32_t downloadRanges_ = @@ -123,6 +126,7 @@ public: * @param ioc The io_context to run on * @param backend BackendInterface implementation * @param subscriptions Subscription manager + * @param randomGenerator A random generator to use for selecting sources * @param validatedLedgers The network validated ledgers datastructure * @param sourceFactory A factory function to create a source */ @@ -131,6 +135,7 @@ public: boost::asio::io_context& ioc, std::shared_ptr backend, std::shared_ptr subscriptions, + std::unique_ptr randomGenerator, std::shared_ptr validatedLedgers, SourceFactory sourceFactory = makeSource ); @@ -142,6 +147,7 @@ public: * @param ioc The io_context to run on * @param backend BackendInterface implementation * @param subscriptions Subscription manager + * @param randomGenerator A random generator to use for selecting sources * @param validatedLedgers The network validated ledgers datastructure * @param sourceFactory A factory function to create a source * @return A shared pointer to a new instance of LoadBalancer @@ -152,6 +158,7 @@ public: boost::asio::io_context& ioc, std::shared_ptr backend, std::shared_ptr subscriptions, + std::unique_ptr randomGenerator, std::shared_ptr validatedLedgers, SourceFactory sourceFactory = makeSource ); diff --git a/src/util/Random.cpp b/src/util/Random.cpp index 73feaf925..3c0e5ad26 100644 --- a/src/util/Random.cpp +++ b/src/util/Random.cpp @@ -25,12 +25,20 @@ namespace util { -void -Random::setSeed(size_t seed) +MTRandomGenerator::MTRandomGenerator() : generator_{std::chrono::system_clock::now().time_since_epoch().count()} { - generator.seed(seed); } -std::mt19937_64 Random::generator{std::chrono::system_clock::now().time_since_epoch().count()}; +size_t +MTRandomGenerator::uniform(size_t min, size_t max) +{ + return uniformImpl(min, max); +} + +void +MTRandomGenerator::setSeed(SeedType seed) +{ + generator_.seed(seed); +} } // namespace util diff --git a/src/util/Random.hpp b/src/util/Random.hpp index 3ce00d51f..bf1bb8345 100644 --- a/src/util/Random.hpp +++ b/src/util/Random.hpp @@ -26,10 +26,52 @@ namespace util { /** - * @brief Random number generator + * @brief Random number generator interface */ -class Random { +class RandomGeneratorInterface { public: + virtual ~RandomGeneratorInterface() = default; + + using SeedType = typename std::mt19937_64::result_type; + + /** + * @brief Generate a random number between min and max + * + * @param min Minimum value + * @param max Maximum value + * @return Random number between min and max + */ + [[nodiscard]] + virtual size_t + uniform(size_t min, size_t max) = 0; + + /** + * @brief Set the seed for the random number generator + * + * @param seed Seed to set + */ + virtual void + setSeed(SeedType seed) = 0; +}; + +/** + * @brief Mersenne Twister random number generator + */ +class MTRandomGenerator : public RandomGeneratorInterface { +public: + MTRandomGenerator(); + + /** + * @brief Generate a random number between min and max + * + * @param min Minimum value + * @param max Maximum value + * @return Random number between min and max + */ + [[nodiscard]] + size_t + uniform(size_t min, size_t max) override; + /** * @brief Generate a random number between min and max * @@ -39,16 +81,17 @@ public: * @return Random number between min and max */ template - static T - uniform(T min, T max) + [[nodiscard]] + T + uniformImpl(T min, T max) { ASSERT(min <= max, "Min cannot be greater than max. min: {}, max: {}", min, max); if constexpr (std::is_floating_point_v) { std::uniform_real_distribution distribution(min, max); - return distribution(generator); + return distribution(generator_); } std::uniform_int_distribution distribution(min, max); - return distribution(generator); + return distribution(generator_); } /** @@ -56,11 +99,11 @@ public: * * @param seed Seed to set */ - static void - setSeed(size_t seed); + void + setSeed(SeedType seed) override; private: - static std::mt19937_64 generator; + std::mt19937_64 generator_; }; } // namespace util diff --git a/tests/common/util/MockRandomGenerator.hpp b/tests/common/util/MockRandomGenerator.hpp new file mode 100644 index 000000000..d5fdef5b8 --- /dev/null +++ b/tests/common/util/MockRandomGenerator.hpp @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2025, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once +#include "util/Random.hpp" + +#include + +struct MockRandomGeneratorImpl : public util::RandomGeneratorInterface { + MOCK_METHOD(size_t, uniform, (size_t min, size_t max), (override)); + MOCK_METHOD(void, setSeed, (SeedType seed), (override)); +}; + +using MockRandomGenerator = testing::NiceMock; +using StrictMockRandomGenerator = testing::StrictMock; diff --git a/tests/integration/data/cassandra/BackendTests.cpp b/tests/integration/data/cassandra/BackendTests.cpp index c69d82e83..9fcc16d3a 100644 --- a/tests/integration/data/cassandra/BackendTests.cpp +++ b/tests/integration/data/cassandra/BackendTests.cpp @@ -624,6 +624,7 @@ TEST_F(BackendCassandraTest, Basic) }; auto generateAccountTx = [&](uint32_t ledgerSequence, auto txns) { std::vector ret; + util::MTRandomGenerator randomGenerator; auto accounts = generateAccounts(ledgerSequence, 10); uint32_t idx = 0; for (auto& [hash, txn, meta] : txns) { @@ -632,7 +633,7 @@ TEST_F(BackendCassandraTest, Basic) data.transactionIndex = idx; data.txHash = hash; for (size_t i = 0; i < 3; ++i) { - data.accounts.insert(accounts[util::Random::uniform(0ul, accounts.size() - 1)]); + data.accounts.insert(accounts[randomGenerator.uniform(0ul, accounts.size() - 1)]); } ++idx; ret.push_back(data); diff --git a/tests/unit/etl/LoadBalancerTests.cpp b/tests/unit/etl/LoadBalancerTests.cpp index d2b530d15..6d4459637 100644 --- a/tests/unit/etl/LoadBalancerTests.cpp +++ b/tests/unit/etl/LoadBalancerTests.cpp @@ -24,6 +24,7 @@ #include "util/MockBackendTestFixture.hpp" #include "util/MockNetworkValidatedLedgers.hpp" #include "util/MockPrometheus.hpp" +#include "util/MockRandomGenerator.hpp" #include "util/MockSource.hpp" #include "util/MockSubscriptionManager.hpp" #include "util/NameGenerator.hpp" @@ -123,17 +124,23 @@ struct LoadBalancerConstructorTests : util::prometheus::WithPrometheus, MockBack makeLoadBalancer() { auto const cfg = getParseLoadBalancerConfig(configJson_); + + auto randomGenerator = std::make_unique(); + randomGenerator_ = randomGenerator.get(); + return std::make_unique( cfg, ioContext_, backend_, subscriptionManager_, + std::move(randomGenerator), networkManager_, [this](auto&&... args) -> SourcePtr { return sourceFactory_(std::forward(args)...); } ); } protected: + MockRandomGenerator* randomGenerator_ = nullptr; StrictMockSubscriptionManagerSharedPtr subscriptionManager_; StrictMockNetworkValidatedLedgersPtr networkManager_; StrictMockSourceFactory sourceFactory_{2}; @@ -430,11 +437,6 @@ TEST_F(LoadBalancer3SourcesTests, forwardingUpdate) } struct LoadBalancerLoadInitialLedgerTests : LoadBalancerOnConnectHookTests { - LoadBalancerLoadInitialLedgerTests() - { - util::Random::setSeed(0); - } - protected: uint32_t const sequence_ = 123; uint32_t const numMarkers_ = 16; @@ -496,7 +498,6 @@ TEST_F(LoadBalancerLoadInitialLedgerCustomNumMarkersTests, loadInitialLedger) EXPECT_CALL(sourceFactory_.sourceAt(1), run); auto loadBalancer = makeLoadBalancer(); - util::Random::setSeed(0); EXPECT_CALL(sourceFactory_.sourceAt(0), hasLedger(sequence_)).WillOnce(Return(true)); EXPECT_CALL(sourceFactory_.sourceAt(0), loadInitialLedger(sequence_, numMarkers_)).WillOnce(Return(response_)); @@ -506,7 +507,6 @@ TEST_F(LoadBalancerLoadInitialLedgerCustomNumMarkersTests, loadInitialLedger) struct LoadBalancerFetchLegerTests : LoadBalancerOnConnectHookTests { LoadBalancerFetchLegerTests() { - util::Random::setSeed(0); response_.second.set_validated(true); } @@ -580,7 +580,6 @@ TEST_F(LoadBalancerFetchLegerTests, fetch_bothSourcesFail) struct LoadBalancerForwardToRippledTests : LoadBalancerConstructorTests, SyncAsioContextTest { LoadBalancerForwardToRippledTests() { - util::Random::setSeed(0); EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(boost::json::object{})); EXPECT_CALL(sourceFactory_.sourceAt(0), run); EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled).WillOnce(Return(boost::json::object{})); @@ -814,6 +813,7 @@ TEST_F(LoadBalancerForwardToRippledTests, onLedgerClosedHookInvalidatesCache) auto const request = boost::json::object{{"command", "server_info"}}; + EXPECT_CALL(*randomGenerator_, uniform(0, 1)).WillOnce(Return(0)).WillOnce(Return(1)); EXPECT_CALL( sourceFactory_.sourceAt(0), forwardToRippled(request, clientIP_, LoadBalancer::kUSER_FORWARDING_X_USER_VALUE, testing::_) diff --git a/tests/unit/etlng/LoadBalancerTests.cpp b/tests/unit/etlng/LoadBalancerTests.cpp index d43b86f5a..b37167ed8 100644 --- a/tests/unit/etlng/LoadBalancerTests.cpp +++ b/tests/unit/etlng/LoadBalancerTests.cpp @@ -26,6 +26,7 @@ #include "util/MockBackendTestFixture.hpp" #include "util/MockNetworkValidatedLedgers.hpp" #include "util/MockPrometheus.hpp" +#include "util/MockRandomGenerator.hpp" #include "util/MockSourceNg.hpp" #include "util/MockSubscriptionManager.hpp" #include "util/NameGenerator.hpp" @@ -144,17 +145,23 @@ struct LoadBalancerConstructorNgTests : util::prometheus::WithPrometheus, MockBa makeLoadBalancer() { auto const cfg = getParseLoadBalancerConfig(configJson_); + + auto randomGenerator = std::make_unique(); + randomGenerator_ = randomGenerator.get(); + return std::make_unique( cfg, ioContext_, backend_, subscriptionManager_, + std::move(randomGenerator), networkManager_, [this](auto&&... args) -> SourcePtr { return sourceFactory_(std::forward(args)...); } ); } protected: + MockRandomGenerator* randomGenerator_ = nullptr; StrictMockSubscriptionManagerSharedPtr subscriptionManager_; StrictMockNetworkValidatedLedgersPtr networkManager_; StrictMockSourceNgFactory sourceFactory_{2}; @@ -450,11 +457,6 @@ TEST_F(LoadBalancer3SourcesNgTests, forwardingUpdate) } struct LoadBalancerLoadInitialLedgerNgTests : LoadBalancerOnConnectHookNgTests { - LoadBalancerLoadInitialLedgerNgTests() - { - util::Random::setSeed(0); - } - protected: uint32_t const sequence_ = 123; uint32_t const numMarkers_ = 16; @@ -522,7 +524,6 @@ TEST_F(LoadBalancerLoadInitialLedgerCustomNumMarkersNgTests, loadInitialLedger) EXPECT_CALL(sourceFactory_.sourceAt(1), run); auto loadBalancer = makeLoadBalancer(); - util::Random::setSeed(0); EXPECT_CALL(sourceFactory_.sourceAt(0), hasLedger(sequence_)).WillOnce(Return(true)); EXPECT_CALL(sourceFactory_.sourceAt(0), loadInitialLedger(sequence_, numMarkers_, testing::_)) .WillOnce(Return(response_)); @@ -533,7 +534,6 @@ TEST_F(LoadBalancerLoadInitialLedgerCustomNumMarkersNgTests, loadInitialLedger) struct LoadBalancerFetchLegerNgTests : LoadBalancerOnConnectHookNgTests { LoadBalancerFetchLegerNgTests() { - util::Random::setSeed(0); response_.second.set_validated(true); } @@ -607,7 +607,6 @@ TEST_F(LoadBalancerFetchLegerNgTests, fetch_bothSourcesFail) struct LoadBalancerForwardToRippledNgTests : LoadBalancerConstructorNgTests, SyncAsioContextTest { LoadBalancerForwardToRippledNgTests() { - util::Random::setSeed(0); EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(boost::json::object{})); EXPECT_CALL(sourceFactory_.sourceAt(0), run); EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled).WillOnce(Return(boost::json::object{})); @@ -841,6 +840,8 @@ TEST_F(LoadBalancerForwardToRippledNgTests, onLedgerClosedHookInvalidatesCache) auto const request = boost::json::object{{"command", "server_info"}}; + EXPECT_CALL(*randomGenerator_, uniform(0, 1)).WillOnce(Return(0)).WillOnce(Return(1)); + EXPECT_CALL( sourceFactory_.sourceAt(0), forwardToRippled(request, clientIP_, LoadBalancer::kUSER_FORWARDING_X_USER_VALUE, testing::_) diff --git a/tests/unit/util/RandomTests.cpp b/tests/unit/util/RandomTests.cpp index 90fb2f5e6..901576446 100644 --- a/tests/unit/util/RandomTests.cpp +++ b/tests/unit/util/RandomTests.cpp @@ -30,18 +30,21 @@ using namespace util; struct RandomTests : public ::testing::Test { static std::vector - generateRandoms(size_t const numRandoms = 1000) + generateRandoms(MTRandomGenerator& randomGenerator, size_t const numRandoms = 1000) { std::vector v; v.reserve(numRandoms); - std::ranges::generate_n(std::back_inserter(v), numRandoms, []() { return Random::uniform(0, 1000); }); + std::ranges::generate_n(std::back_inserter(v), numRandoms, [&randomGenerator]() { + return randomGenerator.uniform(0, 1000); + }); return v; } }; TEST_F(RandomTests, Uniform) { - std::ranges::for_each(generateRandoms(), [](int const& e) { + MTRandomGenerator randomGenerator; + std::ranges::for_each(generateRandoms(randomGenerator), [](int const& e) { EXPECT_GE(e, 0); EXPECT_LE(e, 1000); }); @@ -49,11 +52,13 @@ TEST_F(RandomTests, Uniform) TEST_F(RandomTests, FixedSeed) { - Random::setSeed(42); - std::vector const v1 = generateRandoms(); + MTRandomGenerator randomGenerator; - Random::setSeed(42); - std::vector const v2 = generateRandoms(); + randomGenerator.setSeed(42); + std::vector const v1 = generateRandoms(randomGenerator); + + randomGenerator.setSeed(42); + std::vector const v2 = generateRandoms(randomGenerator); ASSERT_EQ(v1.size(), v2.size()); for (size_t i = 0; i < v1.size(); ++i) { From bfa17134d281b794b01f52191866f5344216a2b1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 30 May 2025 10:54:16 +0100 Subject: [PATCH 11/33] style: clang-tidy auto fixes (#2167) Fixes #2166. Please review and commit clang-tidy fixes. Co-authored-by: godexsoft <385326+godexsoft@users.noreply.github.com> --- src/app/ClioApplication.cpp | 4 +++- tests/unit/etl/LoadBalancerTests.cpp | 1 - tests/unit/etlng/LoadBalancerTests.cpp | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/ClioApplication.cpp b/src/app/ClioApplication.cpp index 61611d1b0..34a754acf 100644 --- a/src/app/ClioApplication.cpp +++ b/src/app/ClioApplication.cpp @@ -36,6 +36,7 @@ #include "rpc/RPCEngine.hpp" #include "rpc/WorkQueue.hpp" #include "rpc/common/impl/HandlerProvider.hpp" +#include "util/Random.hpp" #include "util/async/context/BasicExecutionContext.hpp" #include "util/build/Build.hpp" #include "util/config/ConfigDefinition.hpp" @@ -144,10 +145,11 @@ ClioApplication::run(bool const useNgWebServer) // The server uses the balancer to forward RPCs to a rippled node. // The balancer itself publishes to streams (transactions_proposed and accounts_proposed) auto balancer = [&] -> std::shared_ptr { - if (config_.get("__ng_etl")) + if (config_.get("__ng_etl")) { return etlng::LoadBalancer::makeLoadBalancer( config_, ioc, backend, subscriptions, std::make_unique(), ledgers ); + } return etl::LoadBalancer::makeLoadBalancer( config_, ioc, backend, subscriptions, std::make_unique(), ledgers diff --git a/tests/unit/etl/LoadBalancerTests.cpp b/tests/unit/etl/LoadBalancerTests.cpp index 6d4459637..794738acc 100644 --- a/tests/unit/etl/LoadBalancerTests.cpp +++ b/tests/unit/etl/LoadBalancerTests.cpp @@ -28,7 +28,6 @@ #include "util/MockSource.hpp" #include "util/MockSubscriptionManager.hpp" #include "util/NameGenerator.hpp" -#include "util/Random.hpp" #include "util/config/Array.hpp" #include "util/config/ConfigConstraints.hpp" #include "util/config/ConfigDefinition.hpp" diff --git a/tests/unit/etlng/LoadBalancerTests.cpp b/tests/unit/etlng/LoadBalancerTests.cpp index b37167ed8..1e807c42d 100644 --- a/tests/unit/etlng/LoadBalancerTests.cpp +++ b/tests/unit/etlng/LoadBalancerTests.cpp @@ -30,7 +30,6 @@ #include "util/MockSourceNg.hpp" #include "util/MockSubscriptionManager.hpp" #include "util/NameGenerator.hpp" -#include "util/Random.hpp" #include "util/config/Array.hpp" #include "util/config/ConfigConstraints.hpp" #include "util/config/ConfigDefinition.hpp" From 7588e9d5bf705483ed52fe46c8e9d6a2391794a2 Mon Sep 17 00:00:00 2001 From: Peter Chen <34582813+PeterChen13579@users.noreply.github.com> Date: Fri, 30 May 2025 10:00:40 -0400 Subject: [PATCH 12/33] feat: Support Batch (#2162) fixes #2161. - Tested locally to confirm that Clio forwards Batch transactions correctly. --- src/data/AmendmentCenter.hpp | 1 + src/rpc/RPCCenter.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/data/AmendmentCenter.hpp b/src/data/AmendmentCenter.hpp index 127ecd0b0..a465b544f 100644 --- a/src/data/AmendmentCenter.hpp +++ b/src/data/AmendmentCenter.hpp @@ -139,6 +139,7 @@ struct Amendments { REGISTER(DeepFreeze); REGISTER(PermissionDelegation); REGISTER(fixPayChanCancelAfter); + REGISTER(Batch); // Obsolete but supported by libxrpl REGISTER(CryptoConditionsSuite); diff --git a/src/rpc/RPCCenter.cpp b/src/rpc/RPCCenter.cpp index ce2e9bb11..f74f689bb 100644 --- a/src/rpc/RPCCenter.cpp +++ b/src/rpc/RPCCenter.cpp @@ -85,6 +85,7 @@ forwardedRpcs() "channel_authorize", "channel_verify", "simulate", + "batch" }; return kFORWARDED_RPCS; } From ecdea015b94ead5516d0f93091ebe742d21ed633 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Fri, 30 May 2025 15:50:39 +0100 Subject: [PATCH 13/33] style: Mark JSON literal strings with R"JSON (#2169) --- .../integration/data/BackendFactoryTests.cpp | 10 +- tests/unit/JsonUtilTests.cpp | 8 +- tests/unit/LoggerTests.cpp | 12 +- tests/unit/app/VerifyConfigTests.cpp | 4 +- .../ClusterCommunicationServiceTests.cpp | 2 +- tests/unit/data/BackendCountersTests.cpp | 4 +- .../data/cassandra/SettingsProviderTests.cpp | 16 +- tests/unit/etl/CacheLoaderSettingsTests.cpp | 16 +- tests/unit/etl/CacheLoaderTests.cpp | 8 +- tests/unit/etl/ForwardingSourceTests.cpp | 2 +- tests/unit/etl/LoadBalancerTests.cpp | 16 +- tests/unit/etl/SubscriptionSourceTests.cpp | 40 +- tests/unit/etlng/ETLServiceTests.cpp | 12 +- tests/unit/etlng/ForwardingSourceTests.cpp | 2 +- tests/unit/etlng/LoadBalancerTests.cpp | 16 +- tests/unit/feed/BookChangesFeedTests.cpp | 4 +- tests/unit/feed/ForwardFeedTests.cpp | 2 +- tests/unit/feed/LedgerFeedTests.cpp | 12 +- .../feed/ProposedTransactionFeedTests.cpp | 4 +- tests/unit/feed/SingleFeedBaseTests.cpp | 2 +- tests/unit/feed/SubscriptionManagerTests.cpp | 40 +- tests/unit/feed/TransactionFeedTests.cpp | 36 +- tests/unit/rpc/APIVersionTests.cpp | 28 +- tests/unit/rpc/BaseTests.cpp | 246 +++--- tests/unit/rpc/ForwardingProxyTests.cpp | 36 +- tests/unit/rpc/JsonBoolTests.cpp | 27 +- tests/unit/rpc/RPCHelpersTests.cpp | 180 ++-- tests/unit/rpc/handlers/AMMInfoTests.cpp | 128 +-- .../rpc/handlers/AccountChannelsTests.cpp | 120 +-- .../rpc/handlers/AccountCurrenciesTests.cpp | 32 +- tests/unit/rpc/handlers/AccountInfoTests.cpp | 90 +- tests/unit/rpc/handlers/AccountLinesTests.cpp | 144 ++-- tests/unit/rpc/handlers/AccountNFTsTests.cpp | 82 +- .../unit/rpc/handlers/AccountObjectsTests.cpp | 171 ++-- .../unit/rpc/handlers/AccountOffersTests.cpp | 66 +- tests/unit/rpc/handlers/AccountTxTests.cpp | 372 ++++---- tests/unit/rpc/handlers/BookChangesTests.cpp | 18 +- tests/unit/rpc/handlers/BookOffersTests.cpp | 168 ++-- .../rpc/handlers/DefaultProcessorTests.cpp | 6 +- .../rpc/handlers/DepositAuthorizedTests.cpp | 132 +-- tests/unit/rpc/handlers/FeatureTests.cpp | 62 +- .../rpc/handlers/GatewayBalancesTests.cpp | 96 +-- .../rpc/handlers/GetAggregatePriceTests.cpp | 198 ++--- tests/unit/rpc/handlers/LedgerDataTests.cpp | 104 +-- tests/unit/rpc/handlers/LedgerEntryTests.cpp | 816 +++++++++--------- tests/unit/rpc/handlers/LedgerIndexTests.cpp | 20 +- tests/unit/rpc/handlers/LedgerTests.cpp | 166 ++-- tests/unit/rpc/handlers/MPTHoldersTests.cpp | 110 +-- tests/unit/rpc/handlers/NFTBuyOffersTests.cpp | 100 +-- tests/unit/rpc/handlers/NFTHistoryTests.cpp | 143 +-- tests/unit/rpc/handlers/NFTInfoTests.cpp | 76 +- .../unit/rpc/handlers/NFTSellOffersTests.cpp | 100 +-- tests/unit/rpc/handlers/NFTsByIssuerTest.cpp | 106 +-- .../unit/rpc/handlers/NoRippleCheckTests.cpp | 108 +-- tests/unit/rpc/handlers/PingTests.cpp | 4 +- tests/unit/rpc/handlers/RandomTests.cpp | 2 +- tests/unit/rpc/handlers/ServerInfoTests.cpp | 12 +- tests/unit/rpc/handlers/SubscribeTests.cpp | 174 ++-- tests/unit/rpc/handlers/TestHandlerTests.cpp | 14 +- .../rpc/handlers/TransactionEntryTests.cpp | 34 +- tests/unit/rpc/handlers/TxTests.cpp | 134 +-- tests/unit/rpc/handlers/UnsubscribeTests.cpp | 130 +-- .../unit/rpc/handlers/VersionHandlerTests.cpp | 4 +- .../util/config/ClioConfigDefinitionTests.cpp | 28 +- .../unit/util/config/ConfigFileJsonTests.cpp | 92 +- tests/unit/util/prometheus/CounterTests.cpp | 2 +- tests/unit/util/prometheus/HistogramTests.cpp | 4 +- tests/unit/util/prometheus/LabelTests.cpp | 5 +- tests/unit/web/AdminVerificationTests.cpp | 10 +- tests/unit/web/RPCServerHandlerTests.cpp | 200 ++--- tests/unit/web/ServerTests.cpp | 35 +- tests/unit/web/dosguard/WeightsTests.cpp | 74 +- tests/unit/web/impl/ErrorHandlingTests.cpp | 14 +- tests/unit/web/ng/RPCServerHandlerTests.cpp | 18 +- tests/unit/web/ng/ServerTests.cpp | 22 +- tests/unit/web/ng/impl/ErrorHandlingTests.cpp | 28 +- 76 files changed, 2811 insertions(+), 2748 deletions(-) diff --git a/tests/integration/data/BackendFactoryTests.cpp b/tests/integration/data/BackendFactoryTests.cpp index 604ac0eda..27befa960 100644 --- a/tests/integration/data/BackendFactoryTests.cpp +++ b/tests/integration/data/BackendFactoryTests.cpp @@ -96,21 +96,21 @@ public: TEST_F(BackendCassandraFactoryTest, NoSuchBackend) { - useConfig(R"json( {"database": {"type": "unknown"}} )json"); + useConfig(R"JSON( {"database": {"type": "unknown"}} )JSON"); auto cache = data::LedgerCache{}; EXPECT_THROW(data::makeBackend(cfg_, cache), std::runtime_error); } TEST_F(BackendCassandraFactoryTest, CreateCassandraBackendDBDisconnect) { - useConfig(R"json( + useConfig(R"JSON( {"database": { "type": "cassandra", "cassandra": { "contact_points": "127.0.0.2" } }} - )json"); + )JSON"); auto cache = data::LedgerCache{}; EXPECT_THROW(data::makeBackend(cfg_, cache), std::runtime_error); @@ -146,7 +146,7 @@ TEST_F(BackendCassandraFactoryTestWithDB, CreateCassandraBackend) TEST_F(BackendCassandraFactoryTestWithDB, CreateCassandraBackendReadOnlyWithEmptyDB) { - useConfig(R"json( {"read_only": true} )json"); + useConfig(R"JSON( {"read_only": true} )JSON"); auto cache = data::LedgerCache{}; EXPECT_THROW(data::makeBackend(cfg_, cache), std::runtime_error); } @@ -154,7 +154,7 @@ TEST_F(BackendCassandraFactoryTestWithDB, CreateCassandraBackendReadOnlyWithEmpt TEST_F(BackendCassandraFactoryTestWithDB, CreateCassandraBackendReadOnlyWithDBReady) { auto cfgReadOnly = cfg_; - ASSERT_FALSE(cfgReadOnly.parse(ConfigFileJson{boost::json::parse(R"json( {"read_only": true} )json").as_object()})); + ASSERT_FALSE(cfgReadOnly.parse(ConfigFileJson{boost::json::parse(R"JSON( {"read_only": true} )JSON").as_object()})); auto cache = data::LedgerCache{}; EXPECT_TRUE(data::makeBackend(cfg_, cache)); diff --git a/tests/unit/JsonUtilTests.cpp b/tests/unit/JsonUtilTests.cpp index 38d2995fa..f48d898f6 100644 --- a/tests/unit/JsonUtilTests.cpp +++ b/tests/unit/JsonUtilTests.cpp @@ -24,12 +24,12 @@ TEST(JsonUtils, RemoveSecrets) { - auto json = boost::json::parse(R"({ + auto json = boost::json::parse(R"JSON({ "secret": "snoopy", "seed": "woodstock", "seed_hex": "charlie", "passphrase": "lucy" - })") + })JSON") .as_object(); auto json2 = util::removeSecret(json); @@ -38,7 +38,7 @@ TEST(JsonUtils, RemoveSecrets) EXPECT_EQ(json2.at("seed_hex").as_string(), "*"); EXPECT_EQ(json2.at("passphrase").as_string(), "*"); - json = boost::json::parse(R"({ + json = boost::json::parse(R"JSON({ "params": [ { "secret": "snoopy", @@ -47,7 +47,7 @@ TEST(JsonUtils, RemoveSecrets) "passphrase": "lucy" } ] - })") + })JSON") .as_object(); json2 = util::removeSecret(json); diff --git a/tests/unit/LoggerTests.cpp b/tests/unit/LoggerTests.cpp index eb480f1e6..7549d75a9 100644 --- a/tests/unit/LoggerTests.cpp +++ b/tests/unit/LoggerTests.cpp @@ -137,7 +137,7 @@ TEST_F(LoggerInitTest, DefaultLogLevel) TEST_F(LoggerInitTest, ChannelLogLevel) { - std::string const configStr = R"json( + std::string const configStr = R"JSON( { "log_level": "error", "log_channels": [ @@ -147,7 +147,7 @@ TEST_F(LoggerInitTest, ChannelLogLevel) } ] } - )json"; + )JSON"; auto const parsingErrors = config_.parse(ConfigFileJson{boost::json::parse(configStr).as_object()}); ASSERT_FALSE(parsingErrors.has_value()); @@ -190,7 +190,7 @@ TEST_F(LoggerInitTest, InitReturnsErrorIfCouldNotCreateLogDirectory) TEST_F(LoggerInitTest, InitReturnsErrorIfProvidedInvalidChannel) { - auto const jsonStr = R"json( + auto const jsonStr = R"JSON( { "log_channels": [ { @@ -198,7 +198,7 @@ TEST_F(LoggerInitTest, InitReturnsErrorIfProvidedInvalidChannel) "log_level": "warn" } ] - })json"; + })JSON"; auto const json = boost::json::parse(jsonStr).as_object(); auto const parsingErrors = config_.parse(ConfigFileJson{json}); @@ -216,11 +216,11 @@ TEST_F(LoggerInitTest, LogSizeAndHourRotationCannotBeZero) }; auto const jsonStr = fmt::format( - R"json({{ + R"JSON({{ "{}": 0, "{}": 0, "{}": 0 - }})json", + }})JSON", keys[0], keys[1], keys[2] diff --git a/tests/unit/app/VerifyConfigTests.cpp b/tests/unit/app/VerifyConfigTests.cpp index 6c647ba1b..7ec52fa76 100644 --- a/tests/unit/app/VerifyConfigTests.cpp +++ b/tests/unit/app/VerifyConfigTests.cpp @@ -57,12 +57,12 @@ TEST(VerifyConfigTest, ConfigFileNotExist) TEST(VerifyConfigTest, InvalidJsonFile) { // invalid json because extra "," after 51233 - static constexpr auto kINVALID_JSON = R"({ + static constexpr auto kINVALID_JSON = R"JSON({ "server": { "ip": "0.0.0.0", "port": 51233, } - })"; + })JSON"; auto const tmpConfigFile = TmpFile(kINVALID_JSON); EXPECT_FALSE(parseConfig(tmpConfigFile.path)); diff --git a/tests/unit/cluster/ClusterCommunicationServiceTests.cpp b/tests/unit/cluster/ClusterCommunicationServiceTests.cpp index cdb20f8cc..bef14bfac 100644 --- a/tests/unit/cluster/ClusterCommunicationServiceTests.cpp +++ b/tests/unit/cluster/ClusterCommunicationServiceTests.cpp @@ -195,7 +195,7 @@ TEST_F(ClusterCommunicationServiceTest, Read_Success) EXPECT_CALL(*backend_, fetchClioNodesData).WillOnce([&](auto&&) { std::vector> result = { - {selfUuid, R"json({"update_time": "2015-05-15:12:00:00"})json"}, + {selfUuid, R"JSON({"update_time": "2015-05-15:12:00:00"})JSON"}, }; for (auto const& node : otherNodesData) { diff --git a/tests/unit/data/BackendCountersTests.cpp b/tests/unit/data/BackendCountersTests.cpp index 0bcdd5a60..f544c93fa 100644 --- a/tests/unit/data/BackendCountersTests.cpp +++ b/tests/unit/data/BackendCountersTests.cpp @@ -37,7 +37,7 @@ struct BackendCountersTest : WithPrometheus { static boost::json::object emptyReport() { - return boost::json::parse(R"({ + return boost::json::parse(R"JSON({ "too_busy": 0, "write_sync": 0, "write_sync_retry": 0, @@ -49,7 +49,7 @@ struct BackendCountersTest : WithPrometheus { "read_async_completed": 0, "read_async_retry": 0, "read_async_error": 0 - })") + })JSON") .as_object(); } diff --git a/tests/unit/data/cassandra/SettingsProviderTests.cpp b/tests/unit/data/cassandra/SettingsProviderTests.cpp index 17ea9642d..9820f3b67 100644 --- a/tests/unit/data/cassandra/SettingsProviderTests.cpp +++ b/tests/unit/data/cassandra/SettingsProviderTests.cpp @@ -81,7 +81,7 @@ class SettingsProviderTest : public NoLoggerFixture {}; TEST_F(SettingsProviderTest, Defaults) { - auto const cfg = getParseSettingsConfig(json::parse(R"({"contact_points": "127.0.0.1"})")); + auto const cfg = getParseSettingsConfig(json::parse(R"JSON({"contact_points": "127.0.0.1"})JSON")); SettingsProvider const provider{cfg.getObject("database.cassandra")}; auto const settings = provider.getSettings(); @@ -110,14 +110,14 @@ TEST_F(SettingsProviderTest, Defaults) TEST_F(SettingsProviderTest, SimpleConfig) { - auto const cfg = getParseSettingsConfig(json::parse(R"({ + auto const cfg = getParseSettingsConfig(json::parse(R"JSON({ "database.cassandra.contact_points": "123.123.123.123", "database.cassandra.port": 1234, "database.cassandra.keyspace": "test", "database.cassandra.replication_factor": 42, "database.cassandra.table_prefix": "prefix", "database.cassandra.threads": 24 - })")); + })JSON")); SettingsProvider const provider{cfg.getObject("database.cassandra")}; auto const settings = provider.getSettings(); @@ -135,10 +135,10 @@ TEST_F(SettingsProviderTest, SimpleConfig) TEST_F(SettingsProviderTest, DriverOptionalOptionsSpecified) { - auto const cfg = getParseSettingsConfig(json::parse(R"({ + auto const cfg = getParseSettingsConfig(json::parse(R"JSON({ "database.cassandra.contact_points": "123.123.123.123", "database.cassandra.queue_size_io": 2 - })")); + })JSON")); SettingsProvider const provider{cfg.getObject("database.cassandra")}; auto const settings = provider.getSettings(); @@ -148,7 +148,7 @@ TEST_F(SettingsProviderTest, DriverOptionalOptionsSpecified) TEST_F(SettingsProviderTest, SecureBundleConfig) { auto const cfg = - getParseSettingsConfig(json::parse(R"({"database.cassandra.secure_connect_bundle": "bundleData"})")); + getParseSettingsConfig(json::parse(R"JSON({"database.cassandra.secure_connect_bundle": "bundleData"})JSON")); SettingsProvider const provider{cfg.getObject("database.cassandra")}; auto const settings = provider.getSettings(); @@ -161,10 +161,10 @@ TEST_F(SettingsProviderTest, CertificateConfig) { TmpFile const file{"certificateData"}; auto const cfg = getParseSettingsConfig(json::parse(fmt::format( - R"({{ + R"JSON({{ "database.cassandra.contact_points": "127.0.0.1", "database.cassandra.certfile": "{}" - }})", + }})JSON", file.path ))); SettingsProvider const provider{cfg.getObject("database.cassandra")}; diff --git a/tests/unit/etl/CacheLoaderSettingsTests.cpp b/tests/unit/etl/CacheLoaderSettingsTests.cpp index e3208b25a..1af3a4fd3 100644 --- a/tests/unit/etl/CacheLoaderSettingsTests.cpp +++ b/tests/unit/etl/CacheLoaderSettingsTests.cpp @@ -69,7 +69,7 @@ TEST_F(CacheLoaderSettingsTest, DefaultSettingsParsedCorrectly) TEST_F(CacheLoaderSettingsTest, NumThreadsCorrectlyPropagatedThroughConfig) { - auto const cfg = getParseCacheConfig(json::parse(R"({"io_threads": 42})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"io_threads": 42})JSON")); auto const settings = makeCacheLoaderSettings(cfg); EXPECT_EQ(settings.numThreads, 42); @@ -77,7 +77,7 @@ TEST_F(CacheLoaderSettingsTest, NumThreadsCorrectlyPropagatedThroughConfig) TEST_F(CacheLoaderSettingsTest, NumDiffsCorrectlyPropagatedThroughConfig) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"num_diffs": 42}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"num_diffs": 42}})JSON")); auto const settings = makeCacheLoaderSettings(cfg); EXPECT_EQ(settings.numCacheDiffs, 42); @@ -85,7 +85,7 @@ TEST_F(CacheLoaderSettingsTest, NumDiffsCorrectlyPropagatedThroughConfig) TEST_F(CacheLoaderSettingsTest, NumMarkersCorrectlyPropagatedThroughConfig) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"num_markers": 42}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"num_markers": 42}})JSON")); auto const settings = makeCacheLoaderSettings(cfg); EXPECT_EQ(settings.numCacheMarkers, 42); @@ -93,7 +93,7 @@ TEST_F(CacheLoaderSettingsTest, NumMarkersCorrectlyPropagatedThroughConfig) TEST_F(CacheLoaderSettingsTest, PageFetchSizeCorrectlyPropagatedThroughConfig) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"page_fetch_size": 42}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"page_fetch_size": 42}})JSON")); auto const settings = makeCacheLoaderSettings(cfg); EXPECT_EQ(settings.cachePageFetchSize, 42); @@ -101,7 +101,7 @@ TEST_F(CacheLoaderSettingsTest, PageFetchSizeCorrectlyPropagatedThroughConfig) TEST_F(CacheLoaderSettingsTest, SyncLoadStyleCorrectlyPropagatedThroughConfig) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"load": "sYNC"}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"load": "sYNC"}})JSON")); auto const settings = makeCacheLoaderSettings(cfg); EXPECT_EQ(settings.loadStyle, CacheLoaderSettings::LoadStyle::SYNC); @@ -110,7 +110,7 @@ TEST_F(CacheLoaderSettingsTest, SyncLoadStyleCorrectlyPropagatedThroughConfig) TEST_F(CacheLoaderSettingsTest, AsyncLoadStyleCorrectlyPropagatedThroughConfig) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"load": "aSynC"}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"load": "aSynC"}})JSON")); auto const settings = makeCacheLoaderSettings(cfg); EXPECT_EQ(settings.loadStyle, CacheLoaderSettings::LoadStyle::ASYNC); @@ -120,14 +120,14 @@ TEST_F(CacheLoaderSettingsTest, AsyncLoadStyleCorrectlyPropagatedThroughConfig) TEST_F(CacheLoaderSettingsTest, NoLoadStyleCorrectlyPropagatedThroughConfig) { { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"load": "nONe"}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"load": "nONe"}})JSON")); auto const settings = makeCacheLoaderSettings(cfg); EXPECT_EQ(settings.loadStyle, CacheLoaderSettings::LoadStyle::NONE); EXPECT_TRUE(settings.isDisabled()); } { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"load": "nO"}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"load": "nO"}})JSON")); auto const settings = makeCacheLoaderSettings(cfg); EXPECT_EQ(settings.loadStyle, CacheLoaderSettings::LoadStyle::NONE); diff --git a/tests/unit/etl/CacheLoaderTests.cpp b/tests/unit/etl/CacheLoaderTests.cpp index e9c13d8e7..0867f838f 100644 --- a/tests/unit/etl/CacheLoaderTests.cpp +++ b/tests/unit/etl/CacheLoaderTests.cpp @@ -207,7 +207,7 @@ TEST_P(ParametrizedCacheLoaderTest, CacheDisabledLeadsToCancellation) // TEST_F(CacheLoaderTest, SyncCacheLoaderWaitsTillFullyLoaded) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"load": "sync"}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"load": "sync"}})JSON")); CacheLoader<> loader{cfg, backend_, cache}; auto const diffs = diffProvider.getLatestDiff(); @@ -233,7 +233,7 @@ TEST_F(CacheLoaderTest, SyncCacheLoaderWaitsTillFullyLoaded) TEST_F(CacheLoaderTest, AsyncCacheLoaderCanBeStopped) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"load": "async"}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"load": "async"}})JSON")); CacheLoader loader{cfg, backend_, cache}; auto const diffs = diffProvider.getLatestDiff(); @@ -261,7 +261,7 @@ TEST_F(CacheLoaderTest, AsyncCacheLoaderCanBeStopped) TEST_F(CacheLoaderTest, DisabledCacheLoaderDoesNotLoadCache) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"load": "none"}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"load": "none"}})JSON")); CacheLoader loader{cfg, backend_, cache}; EXPECT_CALL(cache, updateImp).Times(0); @@ -273,7 +273,7 @@ TEST_F(CacheLoaderTest, DisabledCacheLoaderDoesNotLoadCache) TEST_F(CacheLoaderTest, DisabledCacheLoaderCanCallStopAndWait) { - auto const cfg = getParseCacheConfig(json::parse(R"({"cache": {"load": "none"}})")); + auto const cfg = getParseCacheConfig(json::parse(R"JSON({"cache": {"load": "none"}})JSON")); CacheLoader loader{cfg, backend_, cache}; EXPECT_CALL(cache, updateImp).Times(0); diff --git a/tests/unit/etl/ForwardingSourceTests.cpp b/tests/unit/etl/ForwardingSourceTests.cpp index 21a4531b4..60a4e517d 100644 --- a/tests/unit/etl/ForwardingSourceTests.cpp +++ b/tests/unit/etl/ForwardingSourceTests.cpp @@ -71,7 +71,7 @@ struct ForwardingSourceOperationsTests : ForwardingSourceTests { } protected: - std::string const message_ = R"({"data": "some_data"})"; + std::string const message_ = R"JSON({"data": "some_data"})JSON"; boost::json::object const reply_ = {{"reply", "some_reply"}}; }; diff --git a/tests/unit/etl/LoadBalancerTests.cpp b/tests/unit/etl/LoadBalancerTests.cpp index 794738acc..533317235 100644 --- a/tests/unit/etl/LoadBalancerTests.cpp +++ b/tests/unit/etl/LoadBalancerTests.cpp @@ -62,7 +62,7 @@ using namespace util::config; using testing::Return; using namespace util::prometheus; -constexpr static auto const kTWO_SOURCES_LEDGER_RESPONSE = R"({ +constexpr static auto const kTWO_SOURCES_LEDGER_RESPONSE = R"JSON({ "etl_sources": [ { "ip": "127.0.0.1", @@ -75,9 +75,9 @@ constexpr static auto const kTWO_SOURCES_LEDGER_RESPONSE = R"({ "grpc_port": "source2" } ] -})"; +})JSON"; -constexpr static auto const kTHREE_SOURCES_LEDGER_RESPONSE = R"({ +constexpr static auto const kTHREE_SOURCES_LEDGER_RESPONSE = R"JSON({ "etl_sources": [ { "ip": "127.0.0.1", @@ -95,7 +95,7 @@ constexpr static auto const kTHREE_SOURCES_LEDGER_RESPONSE = R"({ "grpc_port": "source3" } ] -})"; +})JSON"; inline static ClioConfigDefinition getParseLoadBalancerConfig(boost::json::value val) @@ -227,8 +227,8 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_Source0Fails1OK) TEST_F(LoadBalancerConstructorTests, fetchETLState_DifferentNetworkID) { - auto const source1Json = boost::json::parse(R"({"result": {"info": {"network_id": 0}}})"); - auto const source2Json = boost::json::parse(R"({"result": {"info": {"network_id": 1}}})"); + auto const source1Json = boost::json::parse(R"JSON({"result": {"info": {"network_id": 0}}})JSON"); + auto const source2Json = boost::json::parse(R"JSON({"result": {"info": {"network_id": 1}}})JSON"); EXPECT_CALL(sourceFactory_, makeSource).Times(2); EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(source1Json.as_object())); @@ -251,8 +251,8 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_AllSourcesFailButAllowNoEtlIs TEST_F(LoadBalancerConstructorTests, fetchETLState_DifferentNetworkIDButAllowNoEtlIsTrue) { - auto const source1Json = boost::json::parse(R"({"result": {"info": {"network_id": 0}}})"); - auto const source2Json = boost::json::parse(R"({"result": {"info": {"network_id": 1}}})"); + auto const source1Json = boost::json::parse(R"JSON({"result": {"info": {"network_id": 0}}})JSON"); + auto const source2Json = boost::json::parse(R"JSON({"result": {"info": {"network_id": 1}}})JSON"); EXPECT_CALL(sourceFactory_, makeSource).Times(2); EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(source1Json.as_object())); EXPECT_CALL(sourceFactory_.sourceAt(0), run); diff --git a/tests/unit/etl/SubscriptionSourceTests.cpp b/tests/unit/etl/SubscriptionSourceTests.cpp index 3814f28c7..e6feebf90 100644 --- a/tests/unit/etl/SubscriptionSourceTests.cpp +++ b/tests/unit/etl/SubscriptionSourceTests.cpp @@ -71,7 +71,7 @@ struct SubscriptionSourceConnectionTestsBase : SyncAsioContextTest { ASSERT_TRUE(message); EXPECT_EQ( message.value(), - R"({"command":"subscribe","streams":["ledger","manifests","validations","transactions_proposed"]})" + R"JSON({"command":"subscribe","streams":["ledger","manifests","validations","transactions_proposed"]})JSON" ); }(); return std::move(connection).value(); @@ -199,7 +199,7 @@ TEST_F(SubscriptionSourceReadTests, GotWrongMessage_Reconnect) TEST_F(SubscriptionSourceReadTests, GotResult) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"result":{})", yield); + auto connection = connectAndSendMessage(R"JSON({"result":{})JSON", yield); connection.close(yield); }); @@ -211,7 +211,7 @@ TEST_F(SubscriptionSourceReadTests, GotResult) TEST_F(SubscriptionSourceReadTests, GotResultWithLedgerIndex) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"result":{"ledger_index":123}})", yield); + auto connection = connectAndSendMessage(R"JSON({"result":{"ledger_index":123}})JSON", yield); connection.close(yield); }); @@ -224,7 +224,7 @@ TEST_F(SubscriptionSourceReadTests, GotResultWithLedgerIndex) TEST_F(SubscriptionSourceReadTests, GotResultWithLedgerIndexAsString_Reconnect) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"result":{"ledger_index":"123"}})", yield); + auto connection = connectAndSendMessage(R"JSON({"result":{"ledger_index":"123"}})JSON", yield); // We have to schedule receiving to receive close frame and boost will handle it automatically connection.receive(yield); serverConnection(yield); @@ -238,7 +238,7 @@ TEST_F(SubscriptionSourceReadTests, GotResultWithLedgerIndexAsString_Reconnect) TEST_F(SubscriptionSourceReadTests, GotResultWithValidatedLedgersAsNumber_Reconnect) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"result":{"validated_ledgers":123}})", yield); + auto connection = connectAndSendMessage(R"JSON({"result":{"validated_ledgers":123}})JSON", yield); // We have to schedule receiving to receive close frame and boost will handle it automatically connection.receive(yield); serverConnection(yield); @@ -262,7 +262,7 @@ TEST_F(SubscriptionSourceReadTests, GotResultWithValidatedLedgers) EXPECT_FALSE(subscriptionSource_.hasLedger(790)); boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"result":{"validated_ledgers":"123-456,789,32"}})", yield); + auto connection = connectAndSendMessage(R"JSON({"result":{"validated_ledgers":"123-456,789,32"}})JSON", yield); connection.close(yield); }); @@ -286,7 +286,7 @@ TEST_F(SubscriptionSourceReadTests, GotResultWithValidatedLedgers) TEST_F(SubscriptionSourceReadTests, GotResultWithValidatedLedgersWrongValue_Reconnect) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"result":{"validated_ledgers":"123-456-789,32"}})", yield); + auto connection = connectAndSendMessage(R"JSON({"result":{"validated_ledgers":"123-456-789,32"}})JSON", yield); // We have to schedule receiving to receive close frame and boost will handle it automatically connection.receive(yield); serverConnection(yield); @@ -306,7 +306,8 @@ TEST_F(SubscriptionSourceReadTests, GotResultWithLedgerIndexAndValidatedLedgers) EXPECT_FALSE(subscriptionSource_.hasLedger(4)); boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"result":{"ledger_index":123,"validated_ledgers":"1-3"}})", yield); + auto connection = + connectAndSendMessage(R"JSON({"result":{"ledger_index":123,"validated_ledgers":"1-3"}})JSON", yield); connection.close(yield); }); @@ -326,7 +327,7 @@ TEST_F(SubscriptionSourceReadTests, GotResultWithLedgerIndexAndValidatedLedgers) TEST_F(SubscriptionSourceReadTests, GotLedgerClosed) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"type":"ledgerClosed"})", yield); + auto connection = connectAndSendMessage(R"JSON({"type":"ledgerClosed"})JSON", yield); connection.close(yield); }); @@ -340,7 +341,7 @@ TEST_F(SubscriptionSourceReadTests, GotLedgerClosedForwardingIsSet) subscriptionSource_.setForwarding(true); boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"type": "ledgerClosed"})", yield); + auto connection = connectAndSendMessage(R"JSON({"type": "ledgerClosed"})JSON", yield); connection.close(yield); }); @@ -356,7 +357,7 @@ TEST_F(SubscriptionSourceReadTests, GotLedgerClosedForwardingIsSet) TEST_F(SubscriptionSourceReadTests, GotLedgerClosedWithLedgerIndex) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"type": "ledgerClosed","ledger_index": 123})", yield); + auto connection = connectAndSendMessage(R"JSON({"type": "ledgerClosed","ledger_index": 123})JSON", yield); connection.close(yield); }); @@ -369,7 +370,7 @@ TEST_F(SubscriptionSourceReadTests, GotLedgerClosedWithLedgerIndex) TEST_F(SubscriptionSourceReadTests, GotLedgerClosedWithLedgerIndexAsString_Reconnect) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"type":"ledgerClosed","ledger_index":"123"}})", yield); + auto connection = connectAndSendMessage(R"JSON({"type":"ledgerClosed","ledger_index":"123"}})JSON", yield); // We have to schedule receiving to receive close frame and boost will handle it automatically connection.receive(yield); serverConnection(yield); @@ -383,7 +384,7 @@ TEST_F(SubscriptionSourceReadTests, GotLedgerClosedWithLedgerIndexAsString_Recon TEST_F(SubscriptionSourceReadTests, GorLedgerClosedWithValidatedLedgersAsNumber_Reconnect) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"type":"ledgerClosed","validated_ledgers":123})", yield); + auto connection = connectAndSendMessage(R"JSON({"type":"ledgerClosed","validated_ledgers":123})JSON", yield); // We have to schedule receiving to receive close frame and boost will handle it automatically connection.receive(yield); serverConnection(yield); @@ -402,7 +403,7 @@ TEST_F(SubscriptionSourceReadTests, GotLedgerClosedWithValidatedLedgers) EXPECT_FALSE(subscriptionSource_.hasLedger(3)); boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"type":"ledgerClosed","validated_ledgers":"1-2"})", yield); + auto connection = connectAndSendMessage(R"JSON({"type":"ledgerClosed","validated_ledgers":"1-2"})JSON", yield); connection.close(yield); }); @@ -425,8 +426,9 @@ TEST_F(SubscriptionSourceReadTests, GotLedgerClosedWithLedgerIndexAndValidatedLe EXPECT_FALSE(subscriptionSource_.hasLedger(3)); boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = - connectAndSendMessage(R"({"type":"ledgerClosed","ledger_index":123,"validated_ledgers":"1-2"})", yield); + auto connection = connectAndSendMessage( + R"JSON({"type":"ledgerClosed","ledger_index":123,"validated_ledgers":"1-2"})JSON", yield + ); connection.close(yield); }); @@ -445,7 +447,7 @@ TEST_F(SubscriptionSourceReadTests, GotLedgerClosedWithLedgerIndexAndValidatedLe TEST_F(SubscriptionSourceReadTests, GotTransactionIsForwardingFalse) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"transaction":"some_transaction_data"})", yield); + auto connection = connectAndSendMessage(R"JSON({"transaction":"some_transaction_data"})JSON", yield); connection.close(yield); }); @@ -489,7 +491,7 @@ TEST_F(SubscriptionSourceReadTests, GotTransactionWithMetaIsForwardingFalse) TEST_F(SubscriptionSourceReadTests, GotValidationReceivedIsForwardingFalse) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"type":"validationReceived"})", yield); + auto connection = connectAndSendMessage(R"JSON({"type":"validationReceived"})JSON", yield); connection.close(yield); }); @@ -517,7 +519,7 @@ TEST_F(SubscriptionSourceReadTests, GotValidationReceivedIsForwardingTrue) TEST_F(SubscriptionSourceReadTests, GotManiefstReceivedIsForwardingFalse) { boost::asio::spawn(ctx_, [this](boost::asio::yield_context yield) { - auto connection = connectAndSendMessage(R"({"type":"manifestReceived"})", yield); + auto connection = connectAndSendMessage(R"JSON({"type":"manifestReceived"})JSON", yield); connection.close(yield); }); diff --git a/tests/unit/etlng/ETLServiceTests.cpp b/tests/unit/etlng/ETLServiceTests.cpp index 6023e061c..dfcffadeb 100644 --- a/tests/unit/etlng/ETLServiceTests.cpp +++ b/tests/unit/etlng/ETLServiceTests.cpp @@ -197,16 +197,16 @@ protected: TEST_F(ETLServiceTests, GetInfoWithoutLastPublish) { - EXPECT_CALL(*balancer_, toJson()).WillOnce(testing::Return(boost::json::parse(R"json([{"test": "value"}])json"))); + EXPECT_CALL(*balancer_, toJson()).WillOnce(testing::Return(boost::json::parse(R"JSON([{"test": "value"}])JSON"))); EXPECT_CALL(*publisher_, getLastPublish()).WillOnce(testing::Return(std::chrono::system_clock::time_point{})); EXPECT_CALL(*publisher_, lastPublishAgeSeconds()).WillRepeatedly(testing::Return(0)); auto result = service_.getInfo(); - auto expectedResult = boost::json::parse(R"json({ + auto expectedResult = boost::json::parse(R"JSON({ "etl_sources": [{"test": "value"}], "is_writer": 0, "read_only": 0 - })json"); + })JSON"); EXPECT_TRUE(result == expectedResult); EXPECT_FALSE(result.contains("last_publish_age_seconds")); @@ -214,17 +214,17 @@ TEST_F(ETLServiceTests, GetInfoWithoutLastPublish) TEST_F(ETLServiceTests, GetInfoWithLastPublish) { - EXPECT_CALL(*balancer_, toJson()).WillOnce(testing::Return(boost::json::parse(R"json([{"test": "value"}])json"))); + EXPECT_CALL(*balancer_, toJson()).WillOnce(testing::Return(boost::json::parse(R"JSON([{"test": "value"}])JSON"))); EXPECT_CALL(*publisher_, getLastPublish()).WillOnce(testing::Return(std::chrono::system_clock::now())); EXPECT_CALL(*publisher_, lastPublishAgeSeconds()).WillOnce(testing::Return(42)); auto result = service_.getInfo(); - auto expectedResult = boost::json::parse(R"json({ + auto expectedResult = boost::json::parse(R"JSON({ "etl_sources": [{"test": "value"}], "is_writer": 0, "read_only": 0, "last_publish_age_seconds": "42" - })json"); + })JSON"); EXPECT_TRUE(result == expectedResult); } diff --git a/tests/unit/etlng/ForwardingSourceTests.cpp b/tests/unit/etlng/ForwardingSourceTests.cpp index 5646cf58c..1e83ed4ab 100644 --- a/tests/unit/etlng/ForwardingSourceTests.cpp +++ b/tests/unit/etlng/ForwardingSourceTests.cpp @@ -71,7 +71,7 @@ struct ForwardingSourceOperationsNgTests : ForwardingSourceNgTests { } protected: - std::string const message_ = R"({"data": "some_data"})"; + std::string const message_ = R"JSON({"data": "some_data"})JSON"; boost::json::object const reply_ = {{"reply", "some_reply"}}; }; diff --git a/tests/unit/etlng/LoadBalancerTests.cpp b/tests/unit/etlng/LoadBalancerTests.cpp index 1e807c42d..715aa7ec6 100644 --- a/tests/unit/etlng/LoadBalancerTests.cpp +++ b/tests/unit/etlng/LoadBalancerTests.cpp @@ -66,7 +66,7 @@ using namespace util::prometheus; namespace { -constinit auto const kTWO_SOURCES_LEDGER_RESPONSE = R"({ +constinit auto const kTWO_SOURCES_LEDGER_RESPONSE = R"JSON({ "etl_sources": [ { "ip": "127.0.0.1", @@ -79,9 +79,9 @@ constinit auto const kTWO_SOURCES_LEDGER_RESPONSE = R"({ "grpc_port": "source2" } ] -})"; +})JSON"; -constinit auto const kTHREE_SOURCES_LEDGER_RESPONSE = R"({ +constinit auto const kTHREE_SOURCES_LEDGER_RESPONSE = R"JSON({ "etl_sources": [ { "ip": "127.0.0.1", @@ -99,7 +99,7 @@ constinit auto const kTHREE_SOURCES_LEDGER_RESPONSE = R"({ "grpc_port": "source3" } ] -})"; +})JSON"; inline ClioConfigDefinition getParseLoadBalancerConfig(boost::json::value val) @@ -247,8 +247,8 @@ TEST_F(LoadBalancerConstructorNgTests, fetchETLState_Source0Fails1OK) TEST_F(LoadBalancerConstructorNgTests, fetchETLState_DifferentNetworkID) { - auto const source1Json = boost::json::parse(R"({"result": {"info": {"network_id": 0}}})"); - auto const source2Json = boost::json::parse(R"({"result": {"info": {"network_id": 1}}})"); + auto const source1Json = boost::json::parse(R"JSON({"result": {"info": {"network_id": 0}}})JSON"); + auto const source2Json = boost::json::parse(R"JSON({"result": {"info": {"network_id": 1}}})JSON"); EXPECT_CALL(sourceFactory_, makeSource).Times(2); EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(source1Json.as_object())); @@ -271,8 +271,8 @@ TEST_F(LoadBalancerConstructorNgTests, fetchETLState_AllSourcesFailButAllowNoEtl TEST_F(LoadBalancerConstructorNgTests, fetchETLState_DifferentNetworkIDButAllowNoEtlIsTrue) { - auto const source1Json = boost::json::parse(R"({"result": {"info": {"network_id": 0}}})"); - auto const source2Json = boost::json::parse(R"({"result": {"info": {"network_id": 1}}})"); + auto const source1Json = boost::json::parse(R"JSON({"result": {"info": {"network_id": 0}}})JSON"); + auto const source2Json = boost::json::parse(R"JSON({"result": {"info": {"network_id": 1}}})JSON"); EXPECT_CALL(sourceFactory_, makeSource).Times(2); EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(source1Json.as_object())); EXPECT_CALL(sourceFactory_.sourceAt(0), run); diff --git a/tests/unit/feed/BookChangesFeedTests.cpp b/tests/unit/feed/BookChangesFeedTests.cpp index d67caa24a..c7480b3de 100644 --- a/tests/unit/feed/BookChangesFeedTests.cpp +++ b/tests/unit/feed/BookChangesFeedTests.cpp @@ -61,7 +61,7 @@ TEST_F(FeedBookChangeTest, Pub) transactions.push_back(trans1); static constexpr auto kBOOK_CHANGE_PUBLISH = - R"({ + R"JSON({ "type":"bookChanges", "ledger_index":32, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", @@ -79,7 +79,7 @@ TEST_F(FeedBookChangeTest, Pub) "close":"-1" } ] - })"; + })JSON"; EXPECT_CALL(*mockSessionPtr, send(sharedStringJsonEq(kBOOK_CHANGE_PUBLISH))).Times(1); testFeedPtr->pub(ledgerHeader, transactions); diff --git a/tests/unit/feed/ForwardFeedTests.cpp b/tests/unit/feed/ForwardFeedTests.cpp index a989c5238..7bbf4fe88 100644 --- a/tests/unit/feed/ForwardFeedTests.cpp +++ b/tests/unit/feed/ForwardFeedTests.cpp @@ -34,7 +34,7 @@ using namespace util::prometheus; namespace { -constexpr auto kFEED = R"({"test":"test"})"; +constexpr auto kFEED = R"JSON({"test":"test"})JSON"; } // namespace diff --git a/tests/unit/feed/LedgerFeedTests.cpp b/tests/unit/feed/LedgerFeedTests.cpp index f2bf50ada..007a91da8 100644 --- a/tests/unit/feed/LedgerFeedTests.cpp +++ b/tests/unit/feed/LedgerFeedTests.cpp @@ -52,7 +52,7 @@ TEST_F(FeedLedgerTest, SubPub) // includes the same fields as a ledger stream message, except that it omits // the type and txn_count fields static constexpr auto kLEDGER_RESPONSE = - R"({ + R"JSON({ "validated_ledgers":"10-30", "ledger_index":30, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", @@ -60,7 +60,7 @@ TEST_F(FeedLedgerTest, SubPub) "fee_base":1, "reserve_base":3, "reserve_inc":2 - })"; + })JSON"; boost::asio::io_context ioContext; boost::asio::spawn(ioContext, [this](boost::asio::yield_context yield) { EXPECT_CALL(*mockSessionPtr, onDisconnect); @@ -72,7 +72,7 @@ TEST_F(FeedLedgerTest, SubPub) EXPECT_EQ(testFeedPtr->count(), 1); static constexpr auto kLEDGER_PUB = - R"({ + R"JSON({ "type":"ledgerClosed", "ledger_index":31, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", @@ -82,7 +82,7 @@ TEST_F(FeedLedgerTest, SubPub) "reserve_inc":0, "validated_ledgers":"10-31", "txn_count":8 - })"; + })JSON"; // test publish EXPECT_CALL(*mockSessionPtr, send(sharedStringJsonEq(kLEDGER_PUB))).Times(1); @@ -107,7 +107,7 @@ TEST_F(FeedLedgerTest, AutoDisconnect) auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0); EXPECT_CALL(*backend_, doFetchLedgerObject).WillOnce(testing::Return(feeBlob)); static constexpr auto kLEDGER_RESPONSE = - R"({ + R"JSON({ "validated_ledgers":"10-30", "ledger_index":30, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", @@ -115,7 +115,7 @@ TEST_F(FeedLedgerTest, AutoDisconnect) "fee_base":1, "reserve_base":3, "reserve_inc":2 - })"; + })JSON"; web::SubscriptionContextInterface::OnDisconnectSlot slot; EXPECT_CALL(*mockSessionPtr, onDisconnect).WillOnce(testing::SaveArg<0>(&slot)); diff --git a/tests/unit/feed/ProposedTransactionFeedTests.cpp b/tests/unit/feed/ProposedTransactionFeedTests.cpp index 3cbf39c9e..3126c5b3a 100644 --- a/tests/unit/feed/ProposedTransactionFeedTests.cpp +++ b/tests/unit/feed/ProposedTransactionFeedTests.cpp @@ -40,7 +40,7 @@ constexpr auto kACCOUNT1 = "rh1HPuRVsYYvThxG2Bs1MfjmrVC73S16Fb"; constexpr auto kACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"; constexpr auto kACCOUNT3 = "r92yNeoiCdwULRbjh6cUBEbD71iHcqe1hE"; constexpr auto kDUMMY_TRANSACTION = - R"({ + R"JSON({ "transaction": { "Account":"rh1HPuRVsYYvThxG2Bs1MfjmrVC73S16Fb", @@ -54,7 +54,7 @@ constexpr auto kDUMMY_TRANSACTION = "TxnSignature":"30450221009BD0D563B24E50B26A42F30455AD21C3D5CD4D80174C41F7B54969FFC08DE94C02201FC35320B56D56D1E34D1D281D48AC68CBEDDD6EE9DFA639CCB08BB251453A87", "hash":"F44393295DB860C6860769C16F5B23887762F09F87A8D1174E0FCFF9E7247F07" } - })"; + })JSON"; } // namespace diff --git a/tests/unit/feed/SingleFeedBaseTests.cpp b/tests/unit/feed/SingleFeedBaseTests.cpp index 2abf19fb0..185660750 100644 --- a/tests/unit/feed/SingleFeedBaseTests.cpp +++ b/tests/unit/feed/SingleFeedBaseTests.cpp @@ -32,7 +32,7 @@ #include namespace { -constexpr auto kFEED = R"({"test":"test"})"; +constexpr auto kFEED = R"JSON({"test":"test"})JSON"; } // namespace using namespace feed::impl; diff --git a/tests/unit/feed/SubscriptionManagerTests.cpp b/tests/unit/feed/SubscriptionManagerTests.cpp index f5d1fd0d9..084937192 100644 --- a/tests/unit/feed/SubscriptionManagerTests.cpp +++ b/tests/unit/feed/SubscriptionManagerTests.cpp @@ -92,8 +92,8 @@ TEST_F(SubscriptionManagerAsyncTest, MultipleThreadCtx) EXPECT_CALL(*sessionPtr_, onDisconnect); subscriptionManagerPtr_->subValidation(session_); - static constexpr auto kJSON_MANIFEST = R"({"manifest":"test"})"; - static constexpr auto kJSON_VALIDATION = R"({"validation":"test"})"; + static constexpr auto kJSON_MANIFEST = R"JSON({"manifest":"test"})JSON"; + static constexpr auto kJSON_VALIDATION = R"JSON({"validation":"test"})JSON"; EXPECT_CALL(*sessionPtr_, send(testing::_)).Times(testing::AtMost(2)); @@ -111,14 +111,14 @@ TEST_F(SubscriptionManagerAsyncTest, MultipleThreadCtxSessionDieEarly) EXPECT_CALL(*sessionPtr_, send(testing::_)).Times(0); session_.reset(); - subscriptionManagerPtr_->forwardManifest(json::parse(R"({"manifest":"test"})").get_object()); - subscriptionManagerPtr_->forwardValidation(json::parse(R"({"validation":"test"})").get_object()); + subscriptionManagerPtr_->forwardManifest(json::parse(R"JSON({"manifest":"test"})JSON").get_object()); + subscriptionManagerPtr_->forwardValidation(json::parse(R"JSON({"validation":"test"})JSON").get_object()); } TEST_F(SubscriptionManagerTest, ReportCurrentSubscriber) { static constexpr auto kREPORT_RETURN = - R"({ + R"JSON({ "ledger":0, "transactions":2, "transactions_proposed":2, @@ -128,7 +128,7 @@ TEST_F(SubscriptionManagerTest, ReportCurrentSubscriber) "accounts_proposed":2, "books":2, "book_changes":2 - })"; + })JSON"; web::SubscriptionContextPtr const session1 = std::make_shared(); MockSession* mockSession1 = dynamic_cast(session1.get()); @@ -200,7 +200,7 @@ TEST_F(SubscriptionManagerTest, ReportCurrentSubscriber) TEST_F(SubscriptionManagerTest, ManifestTest) { - static constexpr auto kDUMMY_MANIFEST = R"({"manifest":"test"})"; + static constexpr auto kDUMMY_MANIFEST = R"JSON({"manifest":"test"})JSON"; EXPECT_CALL(*sessionPtr_, onDisconnect); EXPECT_CALL(*sessionPtr_, send(sharedStringJsonEq(kDUMMY_MANIFEST))); subscriptionManagerPtr_->subManifest(session_); @@ -213,7 +213,7 @@ TEST_F(SubscriptionManagerTest, ManifestTest) TEST_F(SubscriptionManagerTest, ValidationTest) { - static constexpr auto kDUMMY = R"({"validation":"test"})"; + static constexpr auto kDUMMY = R"JSON({"validation":"test"})JSON"; EXPECT_CALL(*sessionPtr_, onDisconnect); EXPECT_CALL(*sessionPtr_, send(sharedStringJsonEq(kDUMMY))); subscriptionManagerPtr_->subValidation(session_); @@ -240,7 +240,7 @@ TEST_F(SubscriptionManagerTest, BookChangesTest) trans1.metadata = metaObj.getSerializer().peekData(); transactions.push_back(trans1); static constexpr auto kBOOK_CHANGE_PUBLISH = - R"({ + R"JSON({ "type":"bookChanges", "ledger_index":32, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", @@ -258,7 +258,7 @@ TEST_F(SubscriptionManagerTest, BookChangesTest) "close":"-1" } ] - })"; + })JSON"; EXPECT_CALL(*sessionPtr_, send(sharedStringJsonEq(kBOOK_CHANGE_PUBLISH))); subscriptionManagerPtr_->pubBookChanges(ledgerHeader, transactions); @@ -280,7 +280,7 @@ TEST_F(SubscriptionManagerTest, LedgerTest) // includes the same fields as a ledger stream message, except that it omits // the type and txn_count fields static constexpr auto kLEDGER_RESPONSE = - R"({ + R"JSON({ "validated_ledgers":"10-30", "ledger_index":30, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", @@ -288,7 +288,7 @@ TEST_F(SubscriptionManagerTest, LedgerTest) "fee_base":1, "reserve_base":3, "reserve_inc":2 - })"; + })JSON"; boost::asio::io_context ctx; boost::asio::spawn(ctx, [this](boost::asio::yield_context yield) { EXPECT_CALL(*sessionPtr_, onDisconnect); @@ -304,7 +304,7 @@ TEST_F(SubscriptionManagerTest, LedgerTest) auto fee2 = ripple::Fees(); fee2.reserve = 10; static constexpr auto kLEDGER_PUB = - R"({ + R"JSON({ "type":"ledgerClosed", "ledger_index":31, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", @@ -314,7 +314,7 @@ TEST_F(SubscriptionManagerTest, LedgerTest) "reserve_inc":0, "validated_ledgers":"10-31", "txn_count":8 - })"; + })JSON"; EXPECT_CALL(*sessionPtr_, send(sharedStringJsonEq(kLEDGER_PUB))); subscriptionManagerPtr_->pubLedger(ledgerHeader2, fee2, "10-31", 8); @@ -345,7 +345,7 @@ TEST_F(SubscriptionManagerTest, TransactionTest) auto const metaObj = createMetaDataForBookChange(kCURRENCY, kISSUER, 22, 3, 1, 1, 3); trans1.metadata = metaObj.getSerializer().peekData(); static constexpr auto kORDERBOOK_PUBLISH = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -404,7 +404,7 @@ TEST_F(SubscriptionManagerTest, TransactionTest) "engine_result":"tesSUCCESS", "close_time_iso": "2000-01-01T00:00:00Z", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; EXPECT_CALL(*sessionPtr_, send(sharedStringJsonEq(kORDERBOOK_PUBLISH))).Times(3); EXPECT_CALL(*sessionPtr_, apiSubversion).Times(3).WillRepeatedly(testing::Return(1)); subscriptionManagerPtr_->pubTransaction(trans1, ledgerHeader); @@ -427,15 +427,15 @@ TEST_F(SubscriptionManagerTest, ProposedTransactionTest) EXPECT_EQ(subscriptionManagerPtr_->report()["transactions_proposed"], 1); static constexpr auto kDUMMY_TRANSACTION = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Destination":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun" } - })"; + })JSON"; static constexpr auto kORDERBOOK_PUBLISH = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -494,7 +494,7 @@ TEST_F(SubscriptionManagerTest, ProposedTransactionTest) "engine_result":"tesSUCCESS", "close_time_iso": "2000-01-01T00:00:00Z", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; EXPECT_CALL(*sessionPtr_, send(sharedStringJsonEq(kDUMMY_TRANSACTION))).Times(2); EXPECT_CALL(*sessionPtr_, send(sharedStringJsonEq(kORDERBOOK_PUBLISH))).Times(2); subscriptionManagerPtr_->forwardProposedTransaction(json::parse(kDUMMY_TRANSACTION).get_object()); diff --git a/tests/unit/feed/TransactionFeedTests.cpp b/tests/unit/feed/TransactionFeedTests.cpp index 89ad1c087..2d15bb658 100644 --- a/tests/unit/feed/TransactionFeedTests.cpp +++ b/tests/unit/feed/TransactionFeedTests.cpp @@ -60,7 +60,7 @@ constexpr auto kLPTOKEN_CURRENCY = "037C35306B24AAB7FF90848206E003279AA47090"; constexpr auto kNETWORK_ID = 0u; constexpr auto kTRAN_V1 = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -115,10 +115,10 @@ constexpr auto kTRAN_V1 = "engine_result_code":0, "engine_result":"tesSUCCESS", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; constexpr auto kTRAN_V2 = - R"({ + R"JSON({ "tx_json": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -168,7 +168,7 @@ constexpr auto kTRAN_V2 = "engine_result_code":0, "engine_result":"tesSUCCESS", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; } // namespace @@ -366,7 +366,7 @@ TEST_F(FeedTransactionTest, SubBookV1) trans1.metadata = metaObj.getSerializer().peekData(); static constexpr auto kORDERBOOK_PUBLISH = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -424,7 +424,7 @@ TEST_F(FeedTransactionTest, SubBookV1) "engine_result":"tesSUCCESS", "close_time_iso": "2000-01-01T00:00:00Z", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; EXPECT_CALL(*mockSessionPtr, apiSubversion).WillOnce(testing::Return(1)); EXPECT_CALL(*mockSessionPtr, send(sharedStringJsonEq(kORDERBOOK_PUBLISH))).Times(1); @@ -435,7 +435,7 @@ TEST_F(FeedTransactionTest, SubBookV1) trans1.metadata = metaObj.getSerializer().peekData(); static constexpr auto kORDERBOOK_CANCEL_PUBLISH = - R"({ + R"JSON({ "transaction":{ "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Amount":"1", @@ -481,7 +481,7 @@ TEST_F(FeedTransactionTest, SubBookV1) "engine_result":"tesSUCCESS", "close_time_iso": "2000-01-01T00:00:00Z", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; EXPECT_CALL(*mockSessionPtr, apiSubversion).WillOnce(testing::Return(1)); EXPECT_CALL(*mockSessionPtr, send(sharedStringJsonEq(kORDERBOOK_CANCEL_PUBLISH))).Times(1); @@ -489,7 +489,7 @@ TEST_F(FeedTransactionTest, SubBookV1) // trigger by offer create meta data static constexpr auto kORDERBOOK_CREATE_PUBLISH = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -537,7 +537,7 @@ TEST_F(FeedTransactionTest, SubBookV1) "engine_result":"tesSUCCESS", "close_time_iso": "2000-01-01T00:00:00Z", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; metaObj = createMetaDataForCreateOffer(kCURRENCY, kISSUER, 22, 3, 1); trans1.metadata = metaObj.getSerializer().peekData(); @@ -570,7 +570,7 @@ TEST_F(FeedTransactionTest, SubBookV2) trans1.metadata = metaObj.getSerializer().peekData(); static constexpr auto kORDERBOOK_PUBLISH = - R"({ + R"JSON({ "tx_json": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -628,7 +628,7 @@ TEST_F(FeedTransactionTest, SubBookV2) "close_time_iso": "2000-01-01T00:00:00Z", "hash":"51D2AAA6B8E4E16EF22F6424854283D8391B56875858A711B8CE4D5B9A422CC2", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; EXPECT_CALL(*mockSessionPtr, apiSubversion).WillOnce(testing::Return(2)); EXPECT_CALL(*mockSessionPtr, send(sharedStringJsonEq(kORDERBOOK_PUBLISH))).Times(1); @@ -840,7 +840,7 @@ TEST_F(FeedTransactionTest, PubTransactionWithOwnerFund) .WillByDefault(testing::Return(accountRoot.getSerializer().peekData())); static constexpr auto kTRANSACTION_FOR_OWNER_FUND = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -875,7 +875,7 @@ TEST_F(FeedTransactionTest, PubTransactionWithOwnerFund) "close_time_iso": "2000-01-01T00:00:00Z", "engine_result":"tesSUCCESS", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; EXPECT_CALL(*mockSessionPtr, apiSubversion).WillOnce(testing::Return(1)); EXPECT_CALL(*mockSessionPtr, send(sharedStringJsonEq(kTRANSACTION_FOR_OWNER_FUND))).Times(1); @@ -886,7 +886,7 @@ TEST_F(FeedTransactionTest, PubTransactionWithOwnerFund) } static constexpr auto kTRAN_FROZEN = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -920,7 +920,7 @@ static constexpr auto kTRAN_FROZEN = "engine_result_code":0, "engine_result":"tesSUCCESS", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; TEST_F(FeedTransactionTest, PubTransactionOfferCreationFrozenLine) { @@ -1146,7 +1146,7 @@ TEST_F(FeedTransactionTest, PubTransactionWithOwnerFundFrozenLPToken) .WillRepeatedly(testing::Return(ammAccountRoot.getSerializer().peekData())); static constexpr auto kTRANSACTION_FOR_OWNER_FUND = - R"({ + R"JSON({ "transaction": { "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -1181,7 +1181,7 @@ TEST_F(FeedTransactionTest, PubTransactionWithOwnerFundFrozenLPToken) "close_time_iso": "2000-01-01T00:00:00Z", "engine_result":"tesSUCCESS", "engine_result_message":"The transaction was applied. Only final in a validated ledger." - })"; + })JSON"; EXPECT_CALL(*mockSessionPtr, apiSubversion).WillOnce(testing::Return(1)); EXPECT_CALL(*mockSessionPtr, send(sharedStringJsonEq(kTRANSACTION_FOR_OWNER_FUND))).Times(1); diff --git a/tests/unit/rpc/APIVersionTests.cpp b/tests/unit/rpc/APIVersionTests.cpp index 74673af13..b43f7fad4 100644 --- a/tests/unit/rpc/APIVersionTests.cpp +++ b/tests/unit/rpc/APIVersionTests.cpp @@ -52,28 +52,28 @@ TEST_F(RPCAPIVersionTest, ReturnsDefaultVersionIfNotSpecified) TEST_F(RPCAPIVersionTest, ReturnsErrorIfVersionHigherThanMaxSupported) { - auto ver = parser_.parse(json::parse(R"({"api_version": 11})").as_object()); + auto ver = parser_.parse(json::parse(R"JSON({"api_version": 11})JSON").as_object()); EXPECT_FALSE(ver); } TEST_F(RPCAPIVersionTest, ReturnsErrorIfVersionLowerThanMinSupported) { - auto ver = parser_.parse(json::parse(R"({"api_version": 1})").as_object()); + auto ver = parser_.parse(json::parse(R"JSON({"api_version": 1})JSON").as_object()); EXPECT_FALSE(ver); } TEST_F(RPCAPIVersionTest, ReturnsErrorOnWrongType) { { - auto ver = parser_.parse(json::parse(R"({"api_version": null})").as_object()); + auto ver = parser_.parse(json::parse(R"JSON({"api_version": null})JSON").as_object()); EXPECT_FALSE(ver); } { - auto ver = parser_.parse(json::parse(R"({"api_version": "5"})").as_object()); + auto ver = parser_.parse(json::parse(R"JSON({"api_version": "5"})JSON").as_object()); EXPECT_FALSE(ver); } { - auto ver = parser_.parse(json::parse(R"({"api_version": "wrong"})").as_object()); + auto ver = parser_.parse(json::parse(R"JSON({"api_version": "wrong"})JSON").as_object()); EXPECT_FALSE(ver); } } @@ -81,17 +81,17 @@ TEST_F(RPCAPIVersionTest, ReturnsErrorOnWrongType) TEST_F(RPCAPIVersionTest, ReturnsParsedVersionIfAllPreconditionsAreMet) { { - auto ver = parser_.parse(json::parse(R"({"api_version": 2})").as_object()); + auto ver = parser_.parse(json::parse(R"JSON({"api_version": 2})JSON").as_object()); EXPECT_TRUE(ver); EXPECT_EQ(ver.value(), 2u); } { - auto ver = parser_.parse(json::parse(R"({"api_version": 10})").as_object()); + auto ver = parser_.parse(json::parse(R"JSON({"api_version": 10})JSON").as_object()); EXPECT_TRUE(ver); EXPECT_EQ(ver.value(), 10u); } { - auto ver = parser_.parse(json::parse(R"({"api_version": 5})").as_object()); + auto ver = parser_.parse(json::parse(R"JSON({"api_version": 5})JSON").as_object()); EXPECT_TRUE(ver); EXPECT_EQ(ver.value(), 5u); } @@ -108,31 +108,31 @@ TEST_F(RPCAPIVersionTest, GetsValuesFromConfigCorrectly) ProductionAPIVersionParser const configuredParser{cfg.getObject("api_version")}; { - auto ver = configuredParser.parse(json::parse(R"({"api_version": 2})").as_object()); + auto ver = configuredParser.parse(json::parse(R"JSON({"api_version": 2})JSON").as_object()); EXPECT_TRUE(ver); EXPECT_EQ(ver.value(), 2u); } { - auto ver = configuredParser.parse(json::parse(R"({"api_version": 10})").as_object()); + auto ver = configuredParser.parse(json::parse(R"JSON({"api_version": 10})JSON").as_object()); EXPECT_TRUE(ver); EXPECT_EQ(ver.value(), 10u); } { - auto ver = configuredParser.parse(json::parse(R"({"api_version": 5})").as_object()); + auto ver = configuredParser.parse(json::parse(R"JSON({"api_version": 5})JSON").as_object()); EXPECT_TRUE(ver); EXPECT_EQ(ver.value(), 5u); } { - auto ver = configuredParser.parse(json::parse(R"({})").as_object()); + auto ver = configuredParser.parse(json::parse(R"JSON({})JSON").as_object()); EXPECT_TRUE(ver); EXPECT_EQ(ver.value(), kDEFAULT_API_VERSION); } { - auto ver = configuredParser.parse(json::parse(R"({"api_version": 11})").as_object()); + auto ver = configuredParser.parse(json::parse(R"JSON({"api_version": 11})JSON").as_object()); EXPECT_FALSE(ver); } { - auto ver = configuredParser.parse(json::parse(R"({"api_version": 1})").as_object()); + auto ver = configuredParser.parse(json::parse(R"JSON({"api_version": 1})JSON").as_object()); EXPECT_FALSE(ver); } } diff --git a/tests/unit/rpc/BaseTests.cpp b/tests/unit/rpc/BaseTests.cpp index 9327ef92e..51d6fe3c5 100644 --- a/tests/unit/rpc/BaseTests.cpp +++ b/tests/unit/rpc/BaseTests.cpp @@ -97,38 +97,38 @@ TEST_F(RPCBaseTest, TypeValidator) {"arr", Type{}}, }; - auto passingInput = json::parse(R"({ + auto passingInput = json::parse(R"JSON({ "uint": 123, "int": 321, "str": "a string", "double": 1.0, "bool": true, "arr": [] - })"); + })JSON"); ASSERT_TRUE(spec.process(passingInput)); { - auto failingInput = json::parse(R"({ "uint": "a string" })"); + auto failingInput = json::parse(R"JSON({ "uint": "a string" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } { - auto failingInput = json::parse(R"({ "int": "a string" })"); + auto failingInput = json::parse(R"JSON({ "int": "a string" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } { - auto failingInput = json::parse(R"({ "str": 1234 })"); + auto failingInput = json::parse(R"JSON({ "str": 1234 })JSON"); ASSERT_FALSE(spec.process(failingInput)); } { - auto failingInput = json::parse(R"({ "double": "a string" })"); + auto failingInput = json::parse(R"JSON({ "double": "a string" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } { - auto failingInput = json::parse(R"({ "bool": "a string" })"); + auto failingInput = json::parse(R"JSON({ "bool": "a string" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } { - auto failingInput = json::parse(R"({ "arr": "a string" })"); + auto failingInput = json::parse(R"JSON({ "arr": "a string" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } } @@ -140,13 +140,13 @@ TEST_F(RPCBaseTest, TypeValidatorMultipleTypes) {"test", Type{}}, }; - auto passingInput = json::parse(R"({ "test": "1234" })"); + auto passingInput = json::parse(R"JSON({ "test": "1234" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto passingInput2 = json::parse(R"({ "test": 1234 })"); + auto passingInput2 = json::parse(R"JSON({ "test": 1234 })JSON"); ASSERT_TRUE(spec.process(passingInput2)); - auto failingInput = json::parse(R"({ "test": true })"); + auto failingInput = json::parse(R"JSON({ "test": true })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -156,13 +156,13 @@ TEST_F(RPCBaseTest, RequiredValidator) {"required", Required{}}, }; - auto passingInput = json::parse(R"({ "required": "present" })"); + auto passingInput = json::parse(R"JSON({ "required": "present" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto passingInput2 = json::parse(R"({ "required": true })"); + auto passingInput2 = json::parse(R"JSON({ "required": true })JSON"); ASSERT_TRUE(spec.process(passingInput2)); - auto failingInput = json::parse(R"({})"); + auto failingInput = json::parse(R"JSON({})JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -172,19 +172,19 @@ TEST_F(RPCBaseTest, BetweenValidator) {"amount", Between{10u, 20u}}, }; - auto passingInput = json::parse(R"({ "amount": 15 })"); + auto passingInput = json::parse(R"JSON({ "amount": 15 })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto passingInput2 = json::parse(R"({ "amount": 10 })"); + auto passingInput2 = json::parse(R"JSON({ "amount": 10 })JSON"); ASSERT_TRUE(spec.process(passingInput2)); - auto passingInput3 = json::parse(R"({ "amount": 20 })"); + auto passingInput3 = json::parse(R"JSON({ "amount": 20 })JSON"); ASSERT_TRUE(spec.process(passingInput3)); - auto failingInput = json::parse(R"({ "amount": 9 })"); + auto failingInput = json::parse(R"JSON({ "amount": 9 })JSON"); ASSERT_FALSE(spec.process(failingInput)); - auto failingInput2 = json::parse(R"({ "amount": 21 })"); + auto failingInput2 = json::parse(R"JSON({ "amount": 21 })JSON"); ASSERT_FALSE(spec.process(failingInput2)); } @@ -194,13 +194,13 @@ TEST_F(RPCBaseTest, MinValidator) {"amount", Min{6}}, }; - auto passingInput = json::parse(R"({ "amount": 7 })"); + auto passingInput = json::parse(R"JSON({ "amount": 7 })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto passingInput2 = json::parse(R"({ "amount": 6 })"); + auto passingInput2 = json::parse(R"JSON({ "amount": 6 })JSON"); ASSERT_TRUE(spec.process(passingInput2)); - auto failingInput = json::parse(R"({ "amount": 5 })"); + auto failingInput = json::parse(R"JSON({ "amount": 5 })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -210,13 +210,13 @@ TEST_F(RPCBaseTest, MaxValidator) {"amount", Max{6}}, }; - auto passingInput = json::parse(R"({ "amount": 5 })"); + auto passingInput = json::parse(R"JSON({ "amount": 5 })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto passingInput2 = json::parse(R"({ "amount": 6 })"); + auto passingInput2 = json::parse(R"JSON({ "amount": 6 })JSON"); ASSERT_TRUE(spec.process(passingInput2)); - auto failingInput = json::parse(R"({ "amount": 7 })"); + auto failingInput = json::parse(R"JSON({ "amount": 7 })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -226,13 +226,13 @@ TEST_F(RPCBaseTest, OneOfValidator) {"currency", OneOf{"XRP", "USD"}}, }; - auto passingInput = json::parse(R"({ "currency": "XRP" })"); + auto passingInput = json::parse(R"JSON({ "currency": "XRP" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto passingInput2 = json::parse(R"({ "currency": "USD" })"); + auto passingInput2 = json::parse(R"JSON({ "currency": "USD" })JSON"); ASSERT_TRUE(spec.process(passingInput2)); - auto failingInput = json::parse(R"({ "currency": "PRX" })"); + auto failingInput = json::parse(R"JSON({ "currency": "PRX" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -242,10 +242,10 @@ TEST_F(RPCBaseTest, EqualToValidator) {"exact", EqualTo{"CaseSensitive"}}, }; - auto passingInput = json::parse(R"({ "exact": "CaseSensitive" })"); + auto passingInput = json::parse(R"JSON({ "exact": "CaseSensitive" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "exact": "Different" })"); + auto failingInput = json::parse(R"JSON({ "exact": "Different" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -271,16 +271,16 @@ TEST_F(RPCBaseTest, ArrayAtValidator) }; // clang-format on - auto passingInput = json::parse(R"({ "arr": [{"limit": 42}] })"); + auto passingInput = json::parse(R"JSON({ "arr": [{"limit": 42}] })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "arr": [{"limit": "not int"}] })"); + auto failingInput = json::parse(R"JSON({ "arr": [{"limit": "not int"}] })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "arr": [{"limit": 42}] ,"arr2": "not array type" })"); + failingInput = json::parse(R"JSON({ "arr": [{"limit": 42}] ,"arr2": "not array type" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "arr": [] })"); + failingInput = json::parse(R"JSON({ "arr": [] })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -301,28 +301,29 @@ TEST_F(RPCBaseTest, IfTypeValidator) }; // if json object pass - auto passingInput = json::parse(R"({ "mix": {"limit": 42, "limit2": 22} })"); + auto passingInput = json::parse(R"JSON({ "mix": {"limit": 42, "limit2": 22} })JSON"); ASSERT_TRUE(spec.process(passingInput)); // if string pass - passingInput = json::parse(R"({ "mix": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" })"); + passingInput = + json::parse(R"JSON({ "mix": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" })JSON"); ASSERT_TRUE(spec.process(passingInput)); // if json object fail at first requirement - auto failingInput = json::parse(R"({ "mix": {"limit": "not int"} })"); + auto failingInput = json::parse(R"JSON({ "mix": {"limit": "not int"} })JSON"); ASSERT_FALSE(spec.process(failingInput)); // if json object fail at second requirement - failingInput = json::parse(R"({ "mix": {"limit": 22, "limit2": "y"} })"); + failingInput = json::parse(R"JSON({ "mix": {"limit": 22, "limit2": "y"} })JSON"); ASSERT_FALSE(spec.process(failingInput)); // if string fail - failingInput = json::parse(R"({ "mix": "not hash" })"); + failingInput = json::parse(R"JSON({ "mix": "not hash" })JSON"); ASSERT_FALSE(spec.process(failingInput)); // type check fail - failingInput = json::parse(R"({ "mix": 1213 })"); + failingInput = json::parse(R"JSON({ "mix": 1213 })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "mix": {"limit": 42, "limit2": 22} , "mix2": 1213 })"); + failingInput = json::parse(R"JSON({ "mix": {"limit": 42, "limit2": 22} , "mix2": 1213 })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -337,18 +338,18 @@ TEST_F(RPCBaseTest, WithCustomError) }; auto passingInput = json::parse( - R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC", "other": "1"})" + R"JSON({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC", "other": "1"})JSON" ); ASSERT_TRUE(spec.process(passingInput)); auto failingInput = - json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515B"})"); + json::parse(R"JSON({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515B"})JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "MyCustomError"); ASSERT_EQ(err.error(), ripple::rpcBAD_FEATURE); - failingInput = json::parse(R"({ "other": 1})"); + failingInput = json::parse(R"JSON({ "other": 1})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "MyCustomError2"); @@ -361,31 +362,31 @@ TEST_F(RPCBaseTest, TimeFormatValidator) {"date", TimeFormatValidator{"%Y-%m-%dT%H:%M:%SZ"}}, }; - auto passingInput = json::parse(R"({ "date": "2023-01-01T00:00:00Z" })"); + auto passingInput = json::parse(R"JSON({ "date": "2023-01-01T00:00:00Z" })JSON"); EXPECT_TRUE(spec.process(passingInput)); passingInput = json::parse("123"); EXPECT_TRUE(spec.process(passingInput)); // key not exists - passingInput = json::parse(R"({ "date1": "2023-01-01T00:00:00Z" })"); + passingInput = json::parse(R"JSON({ "date1": "2023-01-01T00:00:00Z" })JSON"); EXPECT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "date": "2023-01-01-00:00:00" })"); + auto failingInput = json::parse(R"JSON({ "date": "2023-01-01-00:00:00" })JSON"); auto err = spec.process(failingInput); EXPECT_FALSE(err); EXPECT_EQ(err.error(), ripple::rpcINVALID_PARAMS); - failingInput = json::parse(R"({ "date": "01-01-2024T00:00:00" })"); + failingInput = json::parse(R"JSON({ "date": "01-01-2024T00:00:00" })JSON"); EXPECT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "date": "2024-01-01T29:00:00" })"); + failingInput = json::parse(R"JSON({ "date": "2024-01-01T29:00:00" })JSON"); EXPECT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "date": "" })"); + failingInput = json::parse(R"JSON({ "date": "" })JSON"); EXPECT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "date": 1 })"); + failingInput = json::parse(R"JSON({ "date": 1 })JSON"); err = spec.process(failingInput); EXPECT_FALSE(err); EXPECT_EQ(err.error(), ripple::rpcINVALID_PARAMS); @@ -401,10 +402,10 @@ TEST_F(RPCBaseTest, CustomValidator) {"taker", customFormatCheck}, }; - auto passingInput = json::parse(R"({ "taker": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59" })"); + auto passingInput = json::parse(R"JSON({ "taker": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "taker": "wrongformat" })"); + auto failingInput = json::parse(R"JSON({ "taker": "wrongformat" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -415,13 +416,13 @@ TEST_F(RPCBaseTest, NotSupported) {"getter", NotSupported{}}, }; - auto passingInput = json::parse(R"({ "taker": 2 })"); + auto passingInput = json::parse(R"JSON({ "taker": 2 })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "taker": 123 })"); + auto failingInput = json::parse(R"JSON({ "taker": 123 })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "taker": 2, "getter": 2 })"); + failingInput = json::parse(R"JSON({ "taker": 2, "getter": 2 })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -430,21 +431,21 @@ TEST_F(RPCBaseTest, LedgerIndexValidator) auto spec = RpcSpec{ {"ledgerIndex", CustomValidators::ledgerIndexValidator}, }; - auto passingInput = json::parse(R"({ "ledgerIndex": "validated" })"); + auto passingInput = json::parse(R"JSON({ "ledgerIndex": "validated" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - passingInput = json::parse(R"({ "ledgerIndex": "256" })"); + passingInput = json::parse(R"JSON({ "ledgerIndex": "256" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - passingInput = json::parse(R"({ "ledgerIndex": 256 })"); + passingInput = json::parse(R"JSON({ "ledgerIndex": 256 })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "ledgerIndex": "wrongformat" })"); + auto failingInput = json::parse(R"JSON({ "ledgerIndex": "wrongformat" })JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "ledgerIndexMalformed"); - failingInput = json::parse(R"({ "ledgerIndex": true })"); + failingInput = json::parse(R"JSON({ "ledgerIndex": true })JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "ledgerIndexMalformed"); @@ -455,23 +456,24 @@ TEST_F(RPCBaseTest, AccountValidator) auto spec = RpcSpec{ {"account", CustomValidators::accountValidator}, }; - auto failingInput = json::parse(R"({ "account": 256 })"); + auto failingInput = json::parse(R"JSON({ "account": 256 })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" })"); + failingInput = json::parse(R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "account": "02000000000000000000000000000000000000000000000000000000000000000" })"); + failingInput = + json::parse(R"JSON({ "account": "02000000000000000000000000000000000000000000000000000000000000000" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp?" })"); + failingInput = json::parse(R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp?" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })"); + auto passingInput = json::parse(R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })JSON"); ASSERT_TRUE(spec.process(passingInput)); passingInput = - json::parse(R"({ "account": "020000000000000000000000000000000000000000000000000000000000000000" })"); + json::parse(R"JSON({ "account": "020000000000000000000000000000000000000000000000000000000000000000" })JSON"); ASSERT_TRUE(spec.process(passingInput)); } @@ -480,20 +482,20 @@ TEST_F(RPCBaseTest, AccountBase58Validator) auto spec = RpcSpec{ {"account", CustomValidators::accountBase58Validator}, }; - auto failingInput = json::parse(R"({ "account": 256 })"); + auto failingInput = json::parse(R"JSON({ "account": 256 })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" })"); + failingInput = json::parse(R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" })JSON"); ASSERT_FALSE(spec.process(failingInput)); failingInput = - json::parse(R"({ "account": "020000000000000000000000000000000000000000000000000000000000000000" })"); + json::parse(R"JSON({ "account": "020000000000000000000000000000000000000000000000000000000000000000" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp?" })"); + failingInput = json::parse(R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp?" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })"); + auto passingInput = json::parse(R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })JSON"); ASSERT_TRUE(spec.process(passingInput)); } @@ -502,31 +504,31 @@ TEST_F(RPCBaseTest, AccountMarkerValidator) auto spec = RpcSpec{ {"marker", CustomValidators::accountMarkerValidator}, }; - auto failingInput = json::parse(R"({ "marker": 256 })"); + auto failingInput = json::parse(R"JSON({ "marker": 256 })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "marker": "testtest" })"); + failingInput = json::parse(R"JSON({ "marker": "testtest" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "marker": "ABAB1234:1H" })"); + failingInput = json::parse(R"JSON({ "marker": "ABAB1234:1H" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - auto passingInput = json::parse(R"({ "account": "ABAB1234:123" })"); + auto passingInput = json::parse(R"JSON({ "account": "ABAB1234:123" })JSON"); ASSERT_TRUE(spec.process(passingInput)); } TEST_F(RPCBaseTest, Uint160HexStringValidator) { auto const spec = RpcSpec{{"marker", CustomValidators::uint160HexStringValidator}}; - auto passingInput = json::parse(R"({ "marker": "F609A18102218C75767209946A77523CBD97E225"})"); + auto passingInput = json::parse(R"JSON({ "marker": "F609A18102218C75767209946A77523CBD97E225"})JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "marker": 160})"); + auto failingInput = json::parse(R"JSON({ "marker": 160})JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "markerNotString"); - failingInput = json::parse(R"({ "marker": "F609A18102218C75767209946A77523CBD97E2253515BC"})"); + failingInput = json::parse(R"JSON({ "marker": "F609A18102218C75767209946A77523CBD97E2253515BC"})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "markerMalformed"); @@ -535,15 +537,17 @@ TEST_F(RPCBaseTest, Uint160HexStringValidator) TEST_F(RPCBaseTest, Uint192HexStringValidator) { auto const spec = RpcSpec{{"mpt_issuance_id", CustomValidators::uint192HexStringValidator}}; - auto passingInput = json::parse(R"({ "mpt_issuance_id": "0000012F27A9DE73EAA1E8831FA253E19030A17E2D038198"})"); + auto passingInput = + json::parse(R"JSON({ "mpt_issuance_id": "0000012F27A9DE73EAA1E8831FA253E19030A17E2D038198"})JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "mpt_issuance_id": 192})"); + auto failingInput = json::parse(R"JSON({ "mpt_issuance_id": 192})JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "mpt_issuance_idNotString"); - failingInput = json::parse(R"({ "mpt_issuance_id": "0000012F27A9DE73EAA1E8831FA253E19030A17E2D038198983515BC"})"); + failingInput = + json::parse(R"JSON({ "mpt_issuance_id": "0000012F27A9DE73EAA1E8831FA253E19030A17E2D038198983515BC"})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "mpt_issuance_idMalformed"); @@ -553,15 +557,16 @@ TEST_F(RPCBaseTest, Uint256HexStringValidator) { auto const spec = RpcSpec{{"transaction", CustomValidators::uint256HexStringValidator}}; auto passingInput = - json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC"})"); + json::parse(R"JSON({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC"})JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "transaction": 256})"); + auto failingInput = json::parse(R"JSON({ "transaction": 256})JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "transactionNotString"); - failingInput = json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC"})"); + failingInput = + json::parse(R"JSON({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC"})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "transactionMalformed"); @@ -570,26 +575,26 @@ TEST_F(RPCBaseTest, Uint256HexStringValidator) TEST_F(RPCBaseTest, CurrencyValidator) { auto const spec = RpcSpec{{"currency", CustomValidators::currencyValidator}}; - auto passingInput = json::parse(R"({ "currency": "GBP"})"); + auto passingInput = json::parse(R"JSON({ "currency": "GBP"})JSON"); ASSERT_TRUE(spec.process(passingInput)); - passingInput = json::parse(R"({ "currency": "0158415500000000C1F76FF6ECB0BAC600000000"})"); + passingInput = json::parse(R"JSON({ "currency": "0158415500000000C1F76FF6ECB0BAC600000000"})JSON"); ASSERT_TRUE(spec.process(passingInput)); - passingInput = json::parse(R"({ "currency": "0158415500000000c1f76ff6ecb0bac600000000"})"); + passingInput = json::parse(R"JSON({ "currency": "0158415500000000c1f76ff6ecb0bac600000000"})JSON"); ASSERT_TRUE(spec.process(passingInput)); for (auto const& currency : {"[]<", ">()", "{}|", "?!@", "#$%", "^&*"}) { - passingInput = json::parse(fmt::format(R"({{ "currency" : "{}" }})", currency)); + passingInput = json::parse(fmt::format(R"JSON({{ "currency" : "{}" }})JSON", currency)); ASSERT_TRUE(spec.process(passingInput)); } - auto failingInput = json::parse(R"({ "currency": 256})"); + auto failingInput = json::parse(R"JSON({ "currency": 256})JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "currencyNotString"); - failingInput = json::parse(R"({ "currency": "12314"})"); + failingInput = json::parse(R"JSON({ "currency": "12314"})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "malformedCurrency"); @@ -598,15 +603,15 @@ TEST_F(RPCBaseTest, CurrencyValidator) TEST_F(RPCBaseTest, IssuerValidator) { auto const spec = RpcSpec{{"issuer", CustomValidators::issuerValidator}}; - auto passingInput = json::parse(R"({ "issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})"); + auto passingInput = json::parse(R"JSON({ "issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "issuer": 256})"); + auto failingInput = json::parse(R"JSON({ "issuer": 256})JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); ASSERT_EQ(err.error().message, "issuerNotString"); - failingInput = json::parse(fmt::format(R"({{ "issuer": "{}"}})", toBase58(ripple::noAccount()))); + failingInput = json::parse(fmt::format(R"JSON({{ "issuer": "{}"}})JSON", toBase58(ripple::noAccount()))); err = spec.process(failingInput); ASSERT_FALSE(err); } @@ -615,7 +620,7 @@ TEST_F(RPCBaseTest, SubscribeStreamValidator) { auto const spec = RpcSpec{{"streams", CustomValidators::subscribeStreamValidator}}; auto passingInput = json::parse( - R"({ + R"JSON({ "streams": [ "ledger", @@ -626,19 +631,19 @@ TEST_F(RPCBaseTest, SubscribeStreamValidator) "transactions", "book_changes" ] - })" + })JSON" ); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "streams": 256})"); + auto failingInput = json::parse(R"JSON({ "streams": 256})JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); - failingInput = json::parse(R"({ "streams": ["test"]})"); + failingInput = json::parse(R"JSON({ "streams": ["test"]})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); - failingInput = json::parse(R"({ "streams": [123]})"); + failingInput = json::parse(R"JSON({ "streams": [123]})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); } @@ -646,19 +651,20 @@ TEST_F(RPCBaseTest, SubscribeStreamValidator) TEST_F(RPCBaseTest, SubscribeAccountsValidator) { auto const spec = RpcSpec{{"accounts", CustomValidators::subscribeAccountsValidator}}; - auto passingInput = - json::parse(R"({ "accounts": ["rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"]})"); + auto passingInput = json::parse( + R"JSON({ "accounts": ["rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"]})JSON" + ); ASSERT_TRUE(spec.process(passingInput)); - auto failingInput = json::parse(R"({ "accounts": 256})"); + auto failingInput = json::parse(R"JSON({ "accounts": 256})JSON"); auto err = spec.process(failingInput); ASSERT_FALSE(err); - failingInput = json::parse(R"({ "accounts": ["test"]})"); + failingInput = json::parse(R"JSON({ "accounts": ["test"]})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); - failingInput = json::parse(R"({ "accounts": [123]})"); + failingInput = json::parse(R"JSON({ "accounts": [123]})JSON"); err = spec.process(failingInput); ASSERT_FALSE(err); } @@ -669,14 +675,14 @@ TEST_F(RPCBaseTest, ClampingModifier) {"amount", Clamp{10u, 20u}}, }; - auto passingInput = json::parse(R"({ "amount": 15 })"); + auto passingInput = json::parse(R"JSON({ "amount": 15 })JSON"); ASSERT_TRUE(spec.process(passingInput)); - auto passingInput2 = json::parse(R"({ "amount": 5 })"); + auto passingInput2 = json::parse(R"JSON({ "amount": 5 })JSON"); ASSERT_TRUE(spec.process(passingInput2)); ASSERT_EQ(passingInput2.at("amount").as_uint64(), 10u); // clamped - auto passingInput3 = json::parse(R"({ "amount": 25 })"); + auto passingInput3 = json::parse(R"JSON({ "amount": 25 })JSON"); ASSERT_TRUE(spec.process(passingInput3)); ASSERT_EQ(passingInput3.at("amount").as_uint64(), 20u); // clamped } @@ -687,18 +693,18 @@ TEST_F(RPCBaseTest, ToLowerModifier) {"str", ToLower{}}, }; - auto passingInput = json::parse(R"({ "str": "TesT" })"); + auto passingInput = json::parse(R"JSON({ "str": "TesT" })JSON"); ASSERT_TRUE(spec.process(passingInput)); ASSERT_EQ(passingInput.at("str").as_string(), "test"); - auto passingInput2 = json::parse(R"({ "str2": "TesT" })"); + auto passingInput2 = json::parse(R"JSON({ "str2": "TesT" })JSON"); ASSERT_TRUE(spec.process(passingInput2)); // no str no problem - auto passingInput3 = json::parse(R"({ "str": "already lower case" })"); + auto passingInput3 = json::parse(R"JSON({ "str": "already lower case" })JSON"); ASSERT_TRUE(spec.process(passingInput3)); ASSERT_EQ(passingInput3.at("str").as_string(), "already lower case"); - auto passingInput4 = json::parse(R"({ "str": "" })"); + auto passingInput4 = json::parse(R"JSON({ "str": "" })JSON"); ASSERT_TRUE(spec.process(passingInput4)); // empty str no problem ASSERT_EQ(passingInput4.at("str").as_string(), ""); } @@ -709,23 +715,23 @@ TEST_F(RPCBaseTest, ToNumberModifier) {"str", ToNumber{}}, }; - auto passingInput = json::parse(R"({ "str": [] })"); + auto passingInput = json::parse(R"JSON({ "str": [] })JSON"); ASSERT_TRUE(spec.process(passingInput)); - passingInput = json::parse(R"({ "str2": "TesT" })"); + passingInput = json::parse(R"JSON({ "str2": "TesT" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - passingInput = json::parse(R"([])"); + passingInput = json::parse(R"JSON([])JSON"); ASSERT_TRUE(spec.process(passingInput)); - passingInput = json::parse(R"({ "str": "123" })"); + passingInput = json::parse(R"JSON({ "str": "123" })JSON"); ASSERT_TRUE(spec.process(passingInput)); ASSERT_EQ(passingInput.at("str").as_int64(), 123); - auto failingInput = json::parse(R"({ "str": "ok" })"); + auto failingInput = json::parse(R"JSON({ "str": "ok" })JSON"); ASSERT_FALSE(spec.process(failingInput)); - failingInput = json::parse(R"({ "str": "123.123" })"); + failingInput = json::parse(R"JSON({ "str": "123.123" })JSON"); ASSERT_FALSE(spec.process(failingInput)); } @@ -738,13 +744,13 @@ TEST_F(RPCBaseTest, CustomModifier) }; EXPECT_CALL(mockModifier, Call).WillOnce(testing::Return(MaybeError{})); - auto passingInput = json::parse(R"({ "str": "sss" })"); + auto passingInput = json::parse(R"JSON({ "str": "sss" })JSON"); ASSERT_TRUE(spec.process(passingInput)); - passingInput = json::parse(R"({ "strNotExist": 123 })"); + passingInput = json::parse(R"JSON({ "strNotExist": 123 })JSON"); ASSERT_TRUE(spec.process(passingInput)); // not a json object - passingInput = json::parse(R"([])"); + passingInput = json::parse(R"JSON([])JSON"); ASSERT_TRUE(spec.process(passingInput)); } diff --git a/tests/unit/rpc/ForwardingProxyTests.cpp b/tests/unit/rpc/ForwardingProxyTests.cpp index d1bdeef33..e1b41bcb6 100644 --- a/tests/unit/rpc/ForwardingProxyTests.cpp +++ b/tests/unit/rpc/ForwardingProxyTests.cpp @@ -120,7 +120,7 @@ generateTestValuesForParametersTest() {.testName = "ShouldForwardReturnsTrueIsNotAdminRipplePathFind", .apiVersion = 2u, .method = "ripple_path_find", - .testJson = R"({"force_forward": true})", + .testJson = R"JSON({"force_forward": true})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = !isAdmin, @@ -128,7 +128,7 @@ generateTestValuesForParametersTest() {.testName = "ShouldForwardReturnsTrueIsAdminRipplePathFind", .apiVersion = 2u, .method = "ripple_path_find", - .testJson = R"({"force_forward": true})", + .testJson = R"JSON({"force_forward": true})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = isAdmin, @@ -136,7 +136,7 @@ generateTestValuesForParametersTest() {.testName = "ShouldForwardReturnsTrueIfCurrentLedgerSpecified", .apiVersion = 2u, .method = "anymethod", - .testJson = R"({"ledger_index": "current"})", + .testJson = R"JSON({"ledger_index": "current"})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = !isAdmin, @@ -144,7 +144,7 @@ generateTestValuesForParametersTest() {.testName = "ShouldForwardReturnsTrueIfClosedLedgerSpecified", .apiVersion = 2u, .method = "anymethod", - .testJson = R"({"ledger_index": "closed"})", + .testJson = R"JSON({"ledger_index": "closed"})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = !isAdmin, @@ -152,7 +152,7 @@ generateTestValuesForParametersTest() {.testName = "ShouldForwardReturnsTrueIfAccountInfoWithQueueSpecified", .apiVersion = 2u, .method = "account_info", - .testJson = R"({"queue": true})", + .testJson = R"JSON({"queue": true})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = !isAdmin, @@ -160,7 +160,7 @@ generateTestValuesForParametersTest() {.testName = "ShouldForwardReturnsFalseIfAccountInfoQueueIsFalse", .apiVersion = 2u, .method = "account_info", - .testJson = R"({"queue": false})", + .testJson = R"JSON({"queue": false})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = !isAdmin, @@ -168,7 +168,7 @@ generateTestValuesForParametersTest() {.testName = "ShouldForwardReturnsTrueIfLedgerWithQueueSpecified", .apiVersion = 2u, .method = "ledger", - .testJson = R"({"queue": true})", + .testJson = R"JSON({"queue": true})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = !isAdmin, @@ -176,7 +176,7 @@ generateTestValuesForParametersTest() {.testName = "ShouldForwardReturnsFalseIfLedgerQueueIsFalse", .apiVersion = 2u, .method = "ledger", - .testJson = R"({"queue": false})", + .testJson = R"JSON({"queue": false})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = !isAdmin, @@ -216,7 +216,7 @@ generateTestValuesForParametersTest() {.testName = "ForceForwardTrue", .apiVersion = 1u, .method = "any_method", - .testJson = R"({"force_forward": true})", + .testJson = R"JSON({"force_forward": true})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = isAdmin, @@ -224,7 +224,7 @@ generateTestValuesForParametersTest() {.testName = "ForceForwardFalse", .apiVersion = 1u, .method = "any_method", - .testJson = R"({"force_forward": false})", + .testJson = R"JSON({"force_forward": false})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = isAdmin, @@ -232,7 +232,7 @@ generateTestValuesForParametersTest() {.testName = "ForceForwardNotAdmin", .apiVersion = 1u, .method = "any_method", - .testJson = R"({"force_forward": true})", + .testJson = R"JSON({"force_forward": true})JSON", .mockedIsClioOnly = !isClioOnly, .called = 1, .isAdmin = !isAdmin, @@ -240,7 +240,7 @@ generateTestValuesForParametersTest() {.testName = "ForceForwardSubscribe", .apiVersion = 1u, .method = "subscribe", - .testJson = R"({"force_forward": true})", + .testJson = R"JSON({"force_forward": true})JSON", .mockedIsClioOnly = !isClioOnly, .called = 0, .isAdmin = isAdmin, @@ -248,7 +248,7 @@ generateTestValuesForParametersTest() {.testName = "ForceForwardUnsubscribe", .apiVersion = 1u, .method = "unsubscribe", - .testJson = R"({"force_forward": true})", + .testJson = R"JSON({"force_forward": true})JSON", .mockedIsClioOnly = !isClioOnly, .called = 0, .isAdmin = isAdmin, @@ -256,7 +256,7 @@ generateTestValuesForParametersTest() {.testName = "ForceForwardClioOnly", .apiVersion = 1u, .method = "clio_only_method", - .testJson = R"({"force_forward": true})", + .testJson = R"JSON({"force_forward": true})JSON", .mockedIsClioOnly = isClioOnly, .called = 1, .isAdmin = isAdmin, @@ -299,8 +299,8 @@ TEST_F(RPCForwardingProxyTest, ForwardCallsBalancerWithCorrectParams) auto const rawBalancerPtr = loadBalancer_.get(); auto const apiVersion = 2u; auto const method = "submit"; - auto const params = json::parse(R"({"test": true})"); - auto const forwarded = json::parse(R"({"test": true, "command": "submit"})"); + auto const params = json::parse(R"JSON({"test": true})JSON"); + auto const forwarded = json::parse(R"JSON({"test": true, "command": "submit"})JSON"); EXPECT_CALL( *rawBalancerPtr, forwardToRippled(forwarded.as_object(), std::make_optional(kCLIENT_IP), true, _) @@ -329,8 +329,8 @@ TEST_F(RPCForwardingProxyTest, ForwardingFailYieldsErrorStatus) auto const rawBalancerPtr = loadBalancer_.get(); auto const apiVersion = 2u; auto const method = "submit"; - auto const params = json::parse(R"({"test": true})"); - auto const forwarded = json::parse(R"({"test": true, "command": "submit"})"); + auto const params = json::parse(R"JSON({"test": true})JSON"); + auto const forwarded = json::parse(R"JSON({"test": true, "command": "submit"})JSON"); EXPECT_CALL( *rawBalancerPtr, forwardToRippled(forwarded.as_object(), std::make_optional(kCLIENT_IP), true, _) diff --git a/tests/unit/rpc/JsonBoolTests.cpp b/tests/unit/rpc/JsonBoolTests.cpp index 741ced8e6..2c88ec2c6 100644 --- a/tests/unit/rpc/JsonBoolTests.cpp +++ b/tests/unit/rpc/JsonBoolTests.cpp @@ -42,19 +42,20 @@ public: generateTestValuesForParametersTest() { return std::vector{ - {.testName = "NullValue", .json = R"({ "test_bool": null })", .expectedBool = false}, - {.testName = "BoolTrueValue", .json = R"({ "test_bool": true })", .expectedBool = true}, - {.testName = "BoolFalseValue", .json = R"({ "test_bool": false })", .expectedBool = false}, - {.testName = "IntTrueValue", .json = R"({ "test_bool": 1 })", .expectedBool = true}, - {.testName = "IntFalseValue", .json = R"({ "test_bool": 0 })", .expectedBool = false}, - {.testName = "DoubleTrueValue", .json = R"({ "test_bool": 0.1 })", .expectedBool = true}, - {.testName = "DoubleFalseValue", .json = R"({ "test_bool": 0.0 })", .expectedBool = false}, - {.testName = "StringTrueValue", .json = R"({ "test_bool": "true" })", .expectedBool = true}, - {.testName = "StringFalseValue", .json = R"({ "test_bool": "false" })", .expectedBool = true}, - {.testName = "ArrayTrueValue", .json = R"({ "test_bool": [0] })", .expectedBool = true}, - {.testName = "ArrayFalseValue", .json = R"({ "test_bool": [] })", .expectedBool = false}, - {.testName = "ObjectTrueValue", .json = R"({ "test_bool": { "key": null } })", .expectedBool = true}, - {.testName = "ObjectFalseValue", .json = R"({ "test_bool": {} })", .expectedBool = false} + {.testName = "NullValue", .json = R"JSON({ "test_bool": null })JSON", .expectedBool = false}, + {.testName = "BoolTrueValue", .json = R"JSON({ "test_bool": true })JSON", .expectedBool = true}, + {.testName = "BoolFalseValue", .json = R"JSON({ "test_bool": false })JSON", .expectedBool = false}, + {.testName = "IntTrueValue", .json = R"JSON({ "test_bool": 1 })JSON", .expectedBool = true}, + {.testName = "IntFalseValue", .json = R"JSON({ "test_bool": 0 })JSON", .expectedBool = false}, + {.testName = "DoubleTrueValue", .json = R"JSON({ "test_bool": 0.1 })JSON", .expectedBool = true}, + {.testName = "DoubleFalseValue", .json = R"JSON({ "test_bool": 0.0 })JSON", .expectedBool = false}, + {.testName = "StringTrueValue", .json = R"JSON({ "test_bool": "true" })JSON", .expectedBool = true}, + {.testName = "StringFalseValue", .json = R"JSON({ "test_bool": "false" })JSON", .expectedBool = true}, + {.testName = "ArrayTrueValue", .json = R"JSON({ "test_bool": [0] })JSON", .expectedBool = true}, + {.testName = "ArrayFalseValue", .json = R"JSON({ "test_bool": [] })JSON", .expectedBool = false}, + {.testName = "ObjectTrueValue", .json = R"JSON({ "test_bool": { "key": null } })JSON", .expectedBool = true + }, + {.testName = "ObjectFalseValue", .json = R"JSON({ "test_bool": {} })JSON", .expectedBool = false} }; } }; diff --git a/tests/unit/rpc/RPCHelpersTests.cpp b/tests/unit/rpc/RPCHelpersTests.cpp index 02f0df9fc..bd45a0a3b 100644 --- a/tests/unit/rpc/RPCHelpersTests.cpp +++ b/tests/unit/rpc/RPCHelpersTests.cpp @@ -373,28 +373,28 @@ TEST_F(RPCHelpersTest, DecodeInvalidCTID) TEST_F(RPCHelpersTest, DeliverMaxAliasV1) { std::array const inputArray = { - R"({ + R"JSON({ "TransactionType": "Payment", "Amount": { "test": "test" } - })", - R"({ + })JSON", + R"JSON({ "TransactionType": "OfferCreate", "Amount": { "test": "test" } - })", - R"({ + })JSON", + R"JSON({ "TransactionType": "Payment", "Amount1": { "test": "test" } - })" + })JSON" }; std::array outputArray = { - R"({ + R"JSON({ "TransactionType": "Payment", "Amount": { "test": "test" @@ -402,19 +402,19 @@ TEST_F(RPCHelpersTest, DeliverMaxAliasV1) "DeliverMax": { "test": "test" } - })", - R"({ + })JSON", + R"JSON({ "TransactionType": "OfferCreate", "Amount": { "test": "test" } - })", - R"({ + })JSON", + R"JSON({ "TransactionType": "Payment", "Amount1": { "test": "test" } - })" + })JSON" }; for (size_t i = 0; i < inputArray.size(); i++) { @@ -428,12 +428,12 @@ TEST_F(RPCHelpersTest, DeliverMaxAliasV1) TEST_F(RPCHelpersTest, DeliverMaxAliasV2) { auto req = boost::json::parse( - R"({ + R"JSON({ "TransactionType": "Payment", "Amount": { "test": "test" } - })" + })JSON" ) .as_object(); @@ -441,12 +441,12 @@ TEST_F(RPCHelpersTest, DeliverMaxAliasV2) EXPECT_EQ( req, boost::json::parse( - R"({ + R"JSON({ "TransactionType": "Payment", "DeliverMax": { "test": "test" } - })" + })JSON" ) ); } @@ -456,14 +456,14 @@ TEST_F(RPCHelpersTest, LedgerHeaderJson) auto const ledgerHeader = createLedgerHeader(kINDEX1, 30); auto const binJson = toJson(ledgerHeader, true, 1u); - constexpr auto kEXPECT_BIN = R"({ + constexpr auto kEXPECT_BIN = R"JSON({ "ledger_data": "0000001E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "closed": true - })"; + })JSON"; EXPECT_EQ(binJson, boost::json::parse(kEXPECT_BIN)); auto const expectJson = fmt::format( - R"({{ + R"JSON({{ "account_hash": "0000000000000000000000000000000000000000000000000000000000000000", "close_flags": 0, "close_time": 0, @@ -476,7 +476,7 @@ TEST_F(RPCHelpersTest, LedgerHeaderJson) "total_coins": "0", "transaction_hash": "0000000000000000000000000000000000000000000000000000000000000000", "closed": true - }})", + }})JSON", kINDEX1, 30 ); @@ -491,7 +491,7 @@ TEST_F(RPCHelpersTest, LedgerHeaderJsonV2) auto const ledgerHeader = createLedgerHeader(kINDEX1, 30); auto const expectJson = fmt::format( - R"({{ + R"JSON({{ "account_hash": "0000000000000000000000000000000000000000000000000000000000000000", "close_flags": 0, "close_time": 0, @@ -504,7 +504,7 @@ TEST_F(RPCHelpersTest, LedgerHeaderJsonV2) "total_coins": "0", "transaction_hash": "0000000000000000000000000000000000000000000000000000000000000000", "closed": true - }})", + }})JSON", kINDEX1, 30 ); @@ -533,34 +533,34 @@ TEST_F(RPCHelpersTest, TransactionAndMetadataBinaryJsonV2) TEST_F(RPCHelpersTest, ParseIssue) { auto issue = parseIssue(boost::json::parse( - R"({ + R"JSON({ "issuer": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "currency": "JPY" - })" + })JSON" ) .as_object()); EXPECT_TRUE(issue.account == getAccountIdWithString(kACCOUNT2)); - issue = parseIssue(boost::json::parse(R"({"currency": "XRP"})").as_object()); + issue = parseIssue(boost::json::parse(R"JSON({"currency": "XRP"})JSON").as_object()); EXPECT_TRUE(ripple::isXRP(issue.currency)); - EXPECT_THROW(parseIssue(boost::json::parse(R"({"currency": 2})").as_object()), std::runtime_error); + EXPECT_THROW(parseIssue(boost::json::parse(R"JSON({"currency": 2})JSON").as_object()), std::runtime_error); - EXPECT_THROW(parseIssue(boost::json::parse(R"({"currency": "XRP2"})").as_object()), std::runtime_error); + EXPECT_THROW(parseIssue(boost::json::parse(R"JSON({"currency": "XRP2"})JSON").as_object()), std::runtime_error); EXPECT_THROW( parseIssue(boost::json::parse( - R"({ + R"JSON({ "issuer": "abcd", "currency": "JPY" - })" + })JSON" ) .as_object()), std::runtime_error ); EXPECT_THROW( - parseIssue(boost::json::parse(R"({"issuer": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"})").as_object()), + parseIssue(boost::json::parse(R"JSON({"issuer": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"})JSON").as_object()), std::runtime_error ); } @@ -1090,67 +1090,111 @@ static auto generateTestValuesForParametersTest() { return std::vector{ - {.testName = "ledgerEntry", .method = "ledger_entry", .testJson = R"({"type": false})", .expected = false}, + {.testName = "ledgerEntry", + .method = "ledger_entry", + .testJson = R"JSON({"type": false})JSON", + .expected = false}, {.testName = "featureVetoedTrue", .method = "feature", - .testJson = R"({"vetoed": true, "feature": "foo"})", + .testJson = R"JSON({"vetoed": true, "feature": "foo"})JSON", .expected = true}, {.testName = "featureVetoedFalse", .method = "feature", - .testJson = R"({"vetoed": false, "feature": "foo"})", + .testJson = R"JSON({"vetoed": false, "feature": "foo"})JSON", + .expected = true}, + {.testName = "featureVetoedIsStr", + .method = "feature", + .testJson = R"JSON({"vetoed": "String"})JSON", .expected = true}, - {.testName = "featureVetoedIsStr", .method = "feature", .testJson = R"({"vetoed": "String"})", .expected = true - }, - {.testName = "ledger", .method = "ledger", .testJson = R"({})", .expected = false}, - {.testName = "ledgerWithType", .method = "ledger", .testJson = R"({"type": "fee"})", .expected = false}, - {.testName = "ledgerFullTrue", .method = "ledger", .testJson = R"({"full": true})", .expected = true}, - {.testName = "ledgerFullFalse", .method = "ledger", .testJson = R"({"full": false})", .expected = false}, - {.testName = "ledgerFullIsStr", .method = "ledger", .testJson = R"({"full": "String"})", .expected = true}, - {.testName = "ledgerFullIsEmptyStr", .method = "ledger", .testJson = R"({"full": ""})", .expected = false}, - {.testName = "ledgerFullIsNumber1", .method = "ledger", .testJson = R"({"full": 1})", .expected = true}, - {.testName = "ledgerFullIsNumber0", .method = "ledger", .testJson = R"({"full": 0})", .expected = false}, - {.testName = "ledgerFullIsNull", .method = "ledger", .testJson = R"({"full": null})", .expected = false}, - {.testName = "ledgerFullIsFloat0", .method = "ledger", .testJson = R"({"full": 0.0})", .expected = false}, - {.testName = "ledgerFullIsFloat1", .method = "ledger", .testJson = R"({"full": 0.1})", .expected = true}, - {.testName = "ledgerFullIsArray", .method = "ledger", .testJson = R"({"full": [1]})", .expected = true}, - {.testName = "ledgerFullIsEmptyArray", .method = "ledger", .testJson = R"({"full": []})", .expected = false}, - {.testName = "ledgerFullIsObject", .method = "ledger", .testJson = R"({"full": {"key": 1}})", .expected = true}, - {.testName = "ledgerFullIsEmptyObject", .method = "ledger", .testJson = R"({"full": {}})", .expected = false}, - - {.testName = "ledgerAccountsTrue", .method = "ledger", .testJson = R"({"accounts": true})", .expected = true}, - {.testName = "ledgerAccountsFalse", .method = "ledger", .testJson = R"({"accounts": false})", .expected = false + {.testName = "ledger", .method = "ledger", .testJson = R"JSON({})JSON", .expected = false}, + {.testName = "ledgerWithType", .method = "ledger", .testJson = R"JSON({"type": "fee"})JSON", .expected = false}, + {.testName = "ledgerFullTrue", .method = "ledger", .testJson = R"JSON({"full": true})JSON", .expected = true}, + {.testName = "ledgerFullFalse", .method = "ledger", .testJson = R"JSON({"full": false})JSON", .expected = false }, + {.testName = "ledgerFullIsStr", + .method = "ledger", + .testJson = R"JSON({"full": "String"})JSON", + .expected = true}, + {.testName = "ledgerFullIsEmptyStr", + .method = "ledger", + .testJson = R"JSON({"full": ""})JSON", + .expected = false}, + {.testName = "ledgerFullIsNumber1", .method = "ledger", .testJson = R"JSON({"full": 1})JSON", .expected = true}, + {.testName = "ledgerFullIsNumber0", .method = "ledger", .testJson = R"JSON({"full": 0})JSON", .expected = false + }, + {.testName = "ledgerFullIsNull", .method = "ledger", .testJson = R"JSON({"full": null})JSON", .expected = false + }, + {.testName = "ledgerFullIsFloat0", .method = "ledger", .testJson = R"JSON({"full": 0.0})JSON", .expected = false + }, + {.testName = "ledgerFullIsFloat1", .method = "ledger", .testJson = R"JSON({"full": 0.1})JSON", .expected = true + }, + {.testName = "ledgerFullIsArray", .method = "ledger", .testJson = R"JSON({"full": [1]})JSON", .expected = true}, + {.testName = "ledgerFullIsEmptyArray", + .method = "ledger", + .testJson = R"JSON({"full": []})JSON", + .expected = false}, + {.testName = "ledgerFullIsObject", + .method = "ledger", + .testJson = R"JSON({"full": {"key": 1}})JSON", + .expected = true}, + {.testName = "ledgerFullIsEmptyObject", + .method = "ledger", + .testJson = R"JSON({"full": {}})JSON", + .expected = false}, + + {.testName = "ledgerAccountsTrue", + .method = "ledger", + .testJson = R"JSON({"accounts": true})JSON", + .expected = true}, + {.testName = "ledgerAccountsFalse", + .method = "ledger", + .testJson = R"JSON({"accounts": false})JSON", + .expected = false}, {.testName = "ledgerAccountsIsStr", .method = "ledger", - .testJson = R"({"accounts": "String"})", + .testJson = R"JSON({"accounts": "String"})JSON", .expected = true}, {.testName = "ledgerAccountsIsEmptyStr", .method = "ledger", - .testJson = R"({"accounts": ""})", + .testJson = R"JSON({"accounts": ""})JSON", .expected = false}, - {.testName = "ledgerAccountsIsNumber1", .method = "ledger", .testJson = R"({"accounts": 1})", .expected = true}, - {.testName = "ledgerAccountsIsNumber0", .method = "ledger", .testJson = R"({"accounts": 0})", .expected = false - }, - {.testName = "ledgerAccountsIsNull", .method = "ledger", .testJson = R"({"accounts": null})", .expected = false - }, - {.testName = "ledgerAccountsIsFloat0", .method = "ledger", .testJson = R"({"accounts": 0.0})", .expected = false - }, - {.testName = "ledgerAccountsIsFloat1", .method = "ledger", .testJson = R"({"accounts": 0.1})", .expected = true - }, - {.testName = "ledgerAccountsIsArray", .method = "ledger", .testJson = R"({"accounts": [1]})", .expected = true}, + {.testName = "ledgerAccountsIsNumber1", + .method = "ledger", + .testJson = R"JSON({"accounts": 1})JSON", + .expected = true}, + {.testName = "ledgerAccountsIsNumber0", + .method = "ledger", + .testJson = R"JSON({"accounts": 0})JSON", + .expected = false}, + {.testName = "ledgerAccountsIsNull", + .method = "ledger", + .testJson = R"JSON({"accounts": null})JSON", + .expected = false}, + {.testName = "ledgerAccountsIsFloat0", + .method = "ledger", + .testJson = R"JSON({"accounts": 0.0})JSON", + .expected = false}, + {.testName = "ledgerAccountsIsFloat1", + .method = "ledger", + .testJson = R"JSON({"accounts": 0.1})JSON", + .expected = true}, + {.testName = "ledgerAccountsIsArray", + .method = "ledger", + .testJson = R"JSON({"accounts": [1]})JSON", + .expected = true}, {.testName = "ledgerAccountsIsEmptyArray", .method = "ledger", - .testJson = R"({"accounts": []})", + .testJson = R"JSON({"accounts": []})JSON", .expected = false}, {.testName = "ledgerAccountsIsObject", .method = "ledger", - .testJson = R"({"accounts": {"key": 1}})", + .testJson = R"JSON({"accounts": {"key": 1}})JSON", .expected = true}, {.testName = "ledgerAccountsIsEmptyObject", .method = "ledger", - .testJson = R"({"accounts": {}})", + .testJson = R"JSON({"accounts": {}})JSON", .expected = false}, }; } diff --git a/tests/unit/rpc/handlers/AMMInfoTests.cpp b/tests/unit/rpc/handlers/AMMInfoTests.cpp index e9444e7c2..85e32b4ca 100644 --- a/tests/unit/rpc/handlers/AMMInfoTests.cpp +++ b/tests/unit/rpc/handlers/AMMInfoTests.cpp @@ -95,49 +95,49 @@ generateTestValuesForParametersTest() }, AMMInfoParamTestCaseBundle{ .testName = "AMMAccountNotString", - .testJson = R"({"amm_account": 1})", + .testJson = R"JSON({"amm_account": 1})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "Account malformed." }, AMMInfoParamTestCaseBundle{ .testName = "AccountNotString", - .testJson = R"({"account": 1})", + .testJson = R"JSON({"account": 1})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "Account malformed." }, AMMInfoParamTestCaseBundle{ .testName = "AMMAccountInvalid", - .testJson = R"({"amm_account": "xxx"})", + .testJson = R"JSON({"amm_account": "xxx"})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "Account malformed." }, AMMInfoParamTestCaseBundle{ .testName = "AccountInvalid", - .testJson = R"({"account": "xxx"})", + .testJson = R"JSON({"account": "xxx"})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "Account malformed." }, AMMInfoParamTestCaseBundle{ .testName = "AMMAssetNotStringOrObject", - .testJson = R"({"asset": 1})", + .testJson = R"JSON({"asset": 1})JSON", .expectedError = "issueMalformed", .expectedErrorMessage = "Issue is malformed." }, AMMInfoParamTestCaseBundle{ .testName = "AMMAssetEmptyObject", - .testJson = R"({"asset": {}})", + .testJson = R"JSON({"asset": {}})JSON", .expectedError = "issueMalformed", .expectedErrorMessage = "Issue is malformed." }, AMMInfoParamTestCaseBundle{ .testName = "AMMAsset2NotStringOrObject", - .testJson = R"({"asset2": 1})", + .testJson = R"JSON({"asset2": 1})JSON", .expectedError = "issueMalformed", .expectedErrorMessage = "Issue is malformed." }, AMMInfoParamTestCaseBundle{ .testName = "AMMAsset2EmptyObject", - .testJson = R"({"asset2": {}})", + .testJson = R"JSON({"asset2": {}})JSON", .expectedError = "issueMalformed", .expectedErrorMessage = "Issue is malformed." }, @@ -180,10 +180,10 @@ TEST_F(RPCAMMInfoHandlerTest, AccountNotFound) .WillByDefault(Return(accountRoot.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}", "account": "{}" - }})", + }})JSON", kAMM_ACCOUNT, kNOTFOUND_ACCOUNT )); @@ -206,9 +206,9 @@ TEST_F(RPCAMMInfoHandlerTest, AMMAccountNotExist) ON_CALL(*backend_, doFetchLedgerObject).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kWRONG_AMM_ACCOUNT )); @@ -229,9 +229,9 @@ TEST_F(RPCAMMInfoHandlerTest, AMMAccountNotInDBIsMalformed) ON_CALL(*backend_, doFetchLedgerObject).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -255,9 +255,9 @@ TEST_F(RPCAMMInfoHandlerTest, AMMAccountNotFoundMissingAmmField) ON_CALL(*backend_, doFetchLedgerObject).WillByDefault(Return(accountRoot.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -290,9 +290,9 @@ TEST_F(RPCAMMInfoHandlerTest, AMMAccountAmmBlobNotFound) .WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -329,9 +329,9 @@ TEST_F(RPCAMMInfoHandlerTest, AMMAccountAccBlobNotFound) .WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -374,9 +374,9 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathMinimalFirstXRPNoTrustline) ON_CALL(*backend_, doFetchLedgerObject(issue2LineKey, kSEQ, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -384,7 +384,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathMinimalFirstXRPNoTrustline) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -404,7 +404,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathMinimalFirstXRPNoTrustline) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "JPY", @@ -454,10 +454,10 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAccount) .WillByDefault(Return(trustline.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}", "account": "{}" - }})", + }})JSON", kAMM_ACCOUNT, kAMM_ACCOUNT2 )); @@ -466,7 +466,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAccount) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto const expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -486,7 +486,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAccount) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT2, "JPY", @@ -528,9 +528,9 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathMinimalSecondXRPNoTrustline) ON_CALL(*backend_, doFetchLedgerObject(issue2LineKey, kSEQ, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -538,7 +538,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathMinimalSecondXRPNoTrustline) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto const expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -558,7 +558,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathMinimalSecondXRPNoTrustline) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "JPY", @@ -598,9 +598,9 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathNonXRPNoTrustlines) ON_CALL(*backend_, doFetchLedgerObject(issue2LineKey, kSEQ, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -608,7 +608,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathNonXRPNoTrustlines) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto const expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -633,7 +633,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathNonXRPNoTrustlines) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "USD", @@ -687,9 +687,9 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathFrozen) .WillByDefault(Return(trustline2BalanceFrozen.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -697,7 +697,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathFrozen) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto const expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -722,7 +722,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathFrozen) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "USD", @@ -777,9 +777,9 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathFrozenIssuer) .WillByDefault(Return(trustline2BalanceFrozen.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -787,7 +787,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathFrozenIssuer) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto const expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -812,7 +812,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathFrozenIssuer) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "USD", @@ -859,9 +859,9 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithTrustline) .WillByDefault(Return(trustlineBalance.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -869,7 +869,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithTrustline) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -889,7 +889,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithTrustline) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "JPY", @@ -936,9 +936,9 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithVoteSlots) .WillByDefault(Return(trustlineBalance.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -946,7 +946,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithVoteSlots) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -978,7 +978,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithVoteSlots) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "JPY", @@ -1029,9 +1029,9 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAuctionSlot) .WillByDefault(Return(trustlineBalance.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm_account": "{}" - }})", + }})JSON", kAMM_ACCOUNT )); @@ -1039,7 +1039,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAuctionSlot) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -1074,7 +1074,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAuctionSlot) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "JPY", @@ -1117,7 +1117,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAssetsMatchingInputOrder) .WillByDefault(Return(ammObj.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "asset": {{ "currency": "JPY", "issuer": "{}" @@ -1126,7 +1126,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAssetsMatchingInputOrder) "currency": "USD", "issuer": "{}" }} - }})", + }})JSON", kAMM_ACCOUNT, kAMM_ACCOUNT2 )); @@ -1135,7 +1135,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAssetsMatchingInputOrder) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -1179,7 +1179,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAssetsMatchingInputOrder) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "JPY", @@ -1227,7 +1227,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAssetsPreservesInputOrder) .WillByDefault(Return(ammObj.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "asset": {{ "currency": "USD", "issuer": "{}" @@ -1236,7 +1236,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAssetsPreservesInputOrder) "currency": "JPY", "issuer": "{}" }} - }})", + }})JSON", kAMM_ACCOUNT, kAMM_ACCOUNT2 )); @@ -1245,7 +1245,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAssetsPreservesInputOrder) runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); auto expectedResult = json::parse(fmt::format( - R"({{ + R"JSON({{ "amm": {{ "lp_token": {{ "currency": "{}", @@ -1289,7 +1289,7 @@ TEST_F(RPCAMMInfoHandlerTest, HappyPathWithAssetsPreservesInputOrder) "ledger_index": 30, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kLP_ISSUE_CURRENCY, kAMM_ACCOUNT, "USD", diff --git a/tests/unit/rpc/handlers/AccountChannelsTests.cpp b/tests/unit/rpc/handlers/AccountChannelsTests.cpp index 1e60452ae..fc50abf61 100644 --- a/tests/unit/rpc/handlers/AccountChannelsTests.cpp +++ b/tests/unit/rpc/handlers/AccountChannelsTests.cpp @@ -69,10 +69,10 @@ TEST_F(RPCAccountChannelsHandlerTest, LimitNotInt) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": "t" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -88,10 +88,10 @@ TEST_F(RPCAccountChannelsHandlerTest, LimitNegative) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": -1 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -107,10 +107,10 @@ TEST_F(RPCAccountChannelsHandlerTest, LimitZero) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 0 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -126,11 +126,11 @@ TEST_F(RPCAccountChannelsHandlerTest, NonHexLedgerHash) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 10, "ledger_hash": "xxx" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -147,11 +147,11 @@ TEST_F(RPCAccountChannelsHandlerTest, NonStringLedgerHash) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 10, "ledger_hash": 123 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -168,11 +168,11 @@ TEST_F(RPCAccountChannelsHandlerTest, InvalidLedgerIndexString) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 10, "ledger_index": "notvalidated" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -189,10 +189,10 @@ TEST_F(RPCAccountChannelsHandlerTest, MarkerNotString) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "marker": 9 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -212,10 +212,10 @@ TEST_F(RPCAccountChannelsHandlerTest, InvalidMarker) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "marker": "123invalid" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -228,10 +228,10 @@ TEST_F(RPCAccountChannelsHandlerTest, InvalidMarker) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "marker": 401 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -247,9 +247,9 @@ TEST_F(RPCAccountChannelsHandlerTest, AccountInvalidFormat) { runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" - })"); + })JSON"); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -263,9 +263,9 @@ TEST_F(RPCAccountChannelsHandlerTest, AccountNotString) { runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "account": 12 - })"); + })JSON"); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -284,10 +284,10 @@ TEST_F(RPCAccountChannelsHandlerTest, NonExistLedgerViaLedgerHash) EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -309,10 +309,10 @@ TEST_F(RPCAccountChannelsHandlerTest, NonExistLedgerViaLedgerStringIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "4" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -331,10 +331,10 @@ TEST_F(RPCAccountChannelsHandlerTest, NonExistLedgerViaLedgerIntIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": 4 - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -356,10 +356,10 @@ TEST_F(RPCAccountChannelsHandlerTest, NonExistLedgerViaLedgerHash2) ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -380,10 +380,10 @@ TEST_F(RPCAccountChannelsHandlerTest, NonExistLedgerViaLedgerIndex2) // differ from previous logic EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "31" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -406,10 +406,10 @@ TEST_F(RPCAccountChannelsHandlerTest, NonExistAccount) ON_CALL(*backend_, doFetchLedgerObject).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -426,7 +426,7 @@ TEST_F(RPCAccountChannelsHandlerTest, NonExistAccount) // normal case when only provide account TEST_F(RPCAccountChannelsHandlerTest, DefaultParameterTest) { - static constexpr auto kCORRECT_OUTPUT = R"({ + static constexpr auto kCORRECT_OUTPUT = R"JSON({ "account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, @@ -454,7 +454,7 @@ TEST_F(RPCAccountChannelsHandlerTest, DefaultParameterTest) "public_key_hex":"020000000000000000000000000000000000000000000000000000000000000000" } ] - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -484,9 +484,9 @@ TEST_F(RPCAccountChannelsHandlerTest, DefaultParameterTest) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -535,10 +535,10 @@ TEST_F(RPCAccountChannelsHandlerTest, UseLimit) runSpawn([this](auto yield) { auto handler = AnyHandler{AccountChannelsHandler{this->backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 20 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -551,10 +551,10 @@ TEST_F(RPCAccountChannelsHandlerTest, UseLimit) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 9 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -564,10 +564,10 @@ TEST_F(RPCAccountChannelsHandlerTest, UseLimit) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountChannelsHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 401 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -620,11 +620,11 @@ TEST_F(RPCAccountChannelsHandlerTest, UseDestination) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 30, "destination_account":"{}" - }})", + }})JSON", kACCOUNT, kACCOUNT3 )); @@ -657,9 +657,9 @@ TEST_F(RPCAccountChannelsHandlerTest, EmptyChannel) .WillByDefault(Return(ownerDir.getSerializer().peekData())); EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -673,7 +673,7 @@ TEST_F(RPCAccountChannelsHandlerTest, EmptyChannel) // Return expiration cancel_offer source_tag destination_tag when available TEST_F(RPCAccountChannelsHandlerTest, OptionalResponseField) { - static constexpr auto kCORRECT_OUTPUT = R"({ + static constexpr auto kCORRECT_OUTPUT = R"JSON({ "account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, @@ -709,7 +709,7 @@ TEST_F(RPCAccountChannelsHandlerTest, OptionalResponseField) "destination_tag": 400 } ] - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -742,9 +742,9 @@ TEST_F(RPCAccountChannelsHandlerTest, OptionalResponseField) ON_CALL(*backend_, doFetchLedgerObjects).WillByDefault(Return(bbs)); EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -804,10 +804,10 @@ TEST_F(RPCAccountChannelsHandlerTest, MarkerOutput) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": {} - }})", + }})JSON", kACCOUNT, kLIMIT )); @@ -859,11 +859,11 @@ TEST_F(RPCAccountChannelsHandlerTest, MarkerInput) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": {}, "marker": "{},{}" - }})", + }})JSON", kACCOUNT, kLIMIT, kINDEX1, @@ -910,10 +910,10 @@ TEST_F(RPCAccountChannelsHandlerTest, LimitLessThanMin) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": {} - }})", + }})JSON", kACCOUNT, AccountChannelsHandler::kLIMIT_MIN - 1 )); @@ -956,10 +956,10 @@ TEST_F(RPCAccountChannelsHandlerTest, LimitMoreThanMax) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": {} - }})", + }})JSON", kACCOUNT, AccountChannelsHandler::kLIMIT_MAX + 1 )); diff --git a/tests/unit/rpc/handlers/AccountCurrenciesTests.cpp b/tests/unit/rpc/handlers/AccountCurrenciesTests.cpp index f2032fa42..dedb02945 100644 --- a/tests/unit/rpc/handlers/AccountCurrenciesTests.cpp +++ b/tests/unit/rpc/handlers/AccountCurrenciesTests.cpp @@ -74,9 +74,9 @@ TEST_F(RPCAccountCurrenciesHandlerTest, AccountNotExist) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountCurrenciesHandler{backend_}}; @@ -96,9 +96,9 @@ TEST_F(RPCAccountCurrenciesHandlerTest, LedgerNonExistViaIntSequence) ON_CALL(*backend_, fetchLedgerBySequence(30, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountCurrenciesHandler{backend_}}; @@ -120,10 +120,10 @@ TEST_F(RPCAccountCurrenciesHandlerTest, LedgerNonExistViaStringSequence) ON_CALL(*backend_, fetchLedgerBySequence(12, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_index":"{}" - }})", + }})JSON", kACCOUNT, kSEQ )); @@ -145,10 +145,10 @@ TEST_F(RPCAccountCurrenciesHandlerTest, LedgerNonExistViaHash) .WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_hash":"{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -164,7 +164,7 @@ TEST_F(RPCAccountCurrenciesHandlerTest, LedgerNonExistViaHash) TEST_F(RPCAccountCurrenciesHandlerTest, DefaultParameter) { - static constexpr auto kOUTPUT = R"({ + static constexpr auto kOUTPUT = R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -176,7 +176,7 @@ TEST_F(RPCAccountCurrenciesHandlerTest, DefaultParameter) "EUR", "USD" ] - })"; + })JSON"; // return valid ledgerHeader auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); @@ -211,9 +211,9 @@ TEST_F(RPCAccountCurrenciesHandlerTest, DefaultParameter) ON_CALL(*backend_, doFetchLedgerObjects).WillByDefault(Return(bbs)); EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountCurrenciesHandler{backend_}}; @@ -246,10 +246,10 @@ TEST_F(RPCAccountCurrenciesHandlerTest, RequestViaLegderHash) ON_CALL(*backend_, doFetchLedgerObjects).WillByDefault(Return(bbs)); EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_hash":"{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -283,10 +283,10 @@ TEST_F(RPCAccountCurrenciesHandlerTest, RequestViaLegderSeq) ON_CALL(*backend_, doFetchLedgerObjects).WillByDefault(Return(bbs)); EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_index":{} - }})", + }})JSON", kACCOUNT, ledgerSeq )); diff --git a/tests/unit/rpc/handlers/AccountInfoTests.cpp b/tests/unit/rpc/handlers/AccountInfoTests.cpp index 1ec565aec..d67b1169c 100644 --- a/tests/unit/rpc/handlers/AccountInfoTests.cpp +++ b/tests/unit/rpc/handlers/AccountInfoTests.cpp @@ -84,55 +84,55 @@ generateTestValuesForParametersTest() return std::vector{ AccountInfoParamTestCaseBundle{ .testName = "MissingAccountAndIdent", - .testJson = R"({})", + .testJson = R"JSON({})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Missing field 'account'." }, AccountInfoParamTestCaseBundle{ .testName = "AccountNotString", - .testJson = R"({"account":1})", + .testJson = R"JSON({"account":1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountNotString" }, AccountInfoParamTestCaseBundle{ .testName = "AccountInvalid", - .testJson = R"({"account":"xxx"})", + .testJson = R"JSON({"account":"xxx"})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accountMalformed" }, AccountInfoParamTestCaseBundle{ .testName = "IdentNotString", - .testJson = R"({"ident":1})", + .testJson = R"JSON({"ident":1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "identNotString" }, AccountInfoParamTestCaseBundle{ .testName = "IdentInvalid", - .testJson = R"({"ident":"xxx"})", + .testJson = R"JSON({"ident":"xxx"})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "identMalformed" }, AccountInfoParamTestCaseBundle{ .testName = "SignerListsInvalid", - .testJson = R"({"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "signer_lists":1})", + .testJson = R"JSON({"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "signer_lists":1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountInfoParamTestCaseBundle{ .testName = "LedgerHashInvalid", - .testJson = R"({"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":"1"})", + .testJson = R"JSON({"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":"1"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed" }, AccountInfoParamTestCaseBundle{ .testName = "LedgerHashNotString", - .testJson = R"({"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":1})", + .testJson = R"JSON({"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString" }, AccountInfoParamTestCaseBundle{ .testName = "LedgerIndexInvalid", - .testJson = R"({"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_index":"a"})", + .testJson = R"JSON({"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_index":"a"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, @@ -163,9 +163,9 @@ TEST_P(AccountInfoParameterTest, InvalidParams) TEST_F(AccountInfoParameterTest, ApiV1SignerListIsNotBool) { - static constexpr auto kREQ_JSON = R"( + static constexpr auto kREQ_JSON = R"JSON( {"ident":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "signer_lists":1} - )"; + )JSON"; EXPECT_CALL(*backend_, fetchLedgerBySequence); @@ -188,10 +188,10 @@ TEST_F(RPCAccountInfoHandlerTest, LedgerNonExistViaIntSequence) ON_CALL(*backend_, fetchLedgerBySequence(30, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": 30 - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -211,10 +211,10 @@ TEST_F(RPCAccountInfoHandlerTest, LedgerNonExistViaStringSequence) ON_CALL(*backend_, fetchLedgerBySequence(30, _)).WillByDefault(Return(std::nullopt)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "30" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -235,10 +235,10 @@ TEST_F(RPCAccountInfoHandlerTest, LedgerNonExistViaHash) .WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -262,9 +262,9 @@ TEST_F(RPCAccountInfoHandlerTest, AccountNotExist) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -288,9 +288,9 @@ TEST_F(RPCAccountInfoHandlerTest, AccountInvalid) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -322,10 +322,10 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsInvalid) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "signer_lists": true - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -341,7 +341,7 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsInvalid) TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV2) { auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "account_data": {{ "Account": "{}", @@ -400,7 +400,7 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV2) "ledger_hash": "{}", "ledger_index": 30, "validated": true - }})", + }})JSON", kACCOUNT, kINDEX1, kACCOUNT1, @@ -425,10 +425,10 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV2) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "signer_lists": true - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -442,7 +442,7 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV2) TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV1) { auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "account_data": {{ "Account": "{}", @@ -501,7 +501,7 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV1) "ledger_hash": "{}", "ledger_index": 30, "validated": true - }})", + }})JSON", kACCOUNT, kINDEX1, kACCOUNT1, @@ -526,10 +526,10 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV1) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "signer_lists": true - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -543,7 +543,7 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV1) TEST_F(RPCAccountInfoHandlerTest, Flags) { auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "account_data": {{ "Account": "{}", "Balance": "200", @@ -570,7 +570,7 @@ TEST_F(RPCAccountInfoHandlerTest, Flags) "ledger_hash": "{}", "ledger_index": 30, "validated": true - }})", + }})JSON", kACCOUNT, kINDEX1, kLEDGER_HASH @@ -600,9 +600,9 @@ TEST_F(RPCAccountInfoHandlerTest, Flags) EXPECT_CALL(*backend_, doFetchLedgerObject); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -629,9 +629,9 @@ TEST_F(RPCAccountInfoHandlerTest, IdentAndSignerListsFalse) EXPECT_CALL(*backend_, doFetchLedgerObject); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "ident": "{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -645,7 +645,7 @@ TEST_F(RPCAccountInfoHandlerTest, IdentAndSignerListsFalse) TEST_F(RPCAccountInfoHandlerTest, DisallowIncoming) { auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "account_data": {{ "Account": "{}", "Balance": "200", @@ -676,7 +676,7 @@ TEST_F(RPCAccountInfoHandlerTest, DisallowIncoming) "ledger_hash": "{}", "ledger_index": 30, "validated": true - }})", + }})JSON", kACCOUNT, kINDEX1, kLEDGER_HASH @@ -707,9 +707,9 @@ TEST_F(RPCAccountInfoHandlerTest, DisallowIncoming) EXPECT_CALL(*backend_, doFetchLedgerObject); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; @@ -723,7 +723,7 @@ TEST_F(RPCAccountInfoHandlerTest, DisallowIncoming) TEST_F(RPCAccountInfoHandlerTest, Clawback) { auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "account_data": {{ "Account": "{}", "Balance": "200", @@ -751,7 +751,7 @@ TEST_F(RPCAccountInfoHandlerTest, Clawback) "ledger_hash": "{}", "ledger_index": 30, "validated": true - }})", + }})JSON", kACCOUNT, kINDEX1, kLEDGER_HASH @@ -781,9 +781,9 @@ TEST_F(RPCAccountInfoHandlerTest, Clawback) EXPECT_CALL(*backend_, doFetchLedgerObject); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountInfoHandler{backend_, mockAmendmentCenterPtr_}}; diff --git a/tests/unit/rpc/handlers/AccountLinesTests.cpp b/tests/unit/rpc/handlers/AccountLinesTests.cpp index 6c5414d6a..97a4c4a56 100644 --- a/tests/unit/rpc/handlers/AccountLinesTests.cpp +++ b/tests/unit/rpc/handlers/AccountLinesTests.cpp @@ -77,11 +77,11 @@ TEST_F(RPCAccountLinesHandlerTest, NonHexLedgerHash) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 10, "ledger_hash": "xxx" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -98,11 +98,11 @@ TEST_F(RPCAccountLinesHandlerTest, NonStringLedgerHash) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 10, "ledger_hash": 123 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -119,11 +119,11 @@ TEST_F(RPCAccountLinesHandlerTest, InvalidLedgerIndexString) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 10, "ledger_index": "notvalidated" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -140,10 +140,10 @@ TEST_F(RPCAccountLinesHandlerTest, MarkerNotString) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "marker": 9 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -163,10 +163,10 @@ TEST_F(RPCAccountLinesHandlerTest, InvalidMarker) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "marker": "123invalid" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -179,10 +179,10 @@ TEST_F(RPCAccountLinesHandlerTest, InvalidMarker) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "marker": 401 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -199,9 +199,9 @@ TEST_F(RPCAccountLinesHandlerTest, AccountInvalidFormat) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse( - R"({ + R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" - })" + })JSON" ); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -217,9 +217,9 @@ TEST_F(RPCAccountLinesHandlerTest, AccountNotString) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse( - R"({ + R"JSON({ "account": 12 - })" + })JSON" ); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -235,10 +235,10 @@ TEST_F(RPCAccountLinesHandlerTest, PeerInvalidFormat) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse( - R"({ + R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "peer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" - })" + })JSON" ); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -253,10 +253,10 @@ TEST_F(RPCAccountLinesHandlerTest, PeerNotString) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse( - R"({ + R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "peer": 12 - })" + })JSON" ); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -272,10 +272,10 @@ TEST_F(RPCAccountLinesHandlerTest, LimitNotInt) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse( - R"({ + R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "t" - })" + })JSON" ); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -290,10 +290,10 @@ TEST_F(RPCAccountLinesHandlerTest, LimitNegative) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse( - R"({ + R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1 - })" + })JSON" ); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -308,10 +308,10 @@ TEST_F(RPCAccountLinesHandlerTest, LimitZero) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse( - R"({ + R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0 - })" + })JSON" ); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -330,10 +330,10 @@ TEST_F(RPCAccountLinesHandlerTest, NonExistLedgerViaLedgerHash) EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -355,10 +355,10 @@ TEST_F(RPCAccountLinesHandlerTest, NonExistLedgerViaLedgerStringIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "4" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -377,10 +377,10 @@ TEST_F(RPCAccountLinesHandlerTest, NonExistLedgerViaLedgerIntIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": 4 - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -402,10 +402,10 @@ TEST_F(RPCAccountLinesHandlerTest, NonExistLedgerViaLedgerHash2) ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -426,10 +426,10 @@ TEST_F(RPCAccountLinesHandlerTest, NonExistLedgerViaLedgerIndex2) // differ from previous logic EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "31" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -452,10 +452,10 @@ TEST_F(RPCAccountLinesHandlerTest, NonExistAccount) ON_CALL(*backend_, doFetchLedgerObject).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -502,13 +502,13 @@ TEST_F(RPCAccountLinesHandlerTest, DefaultParameterTest) runSpawn([this](auto yield) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); auto const correctOutput = - R"({ + R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, @@ -534,7 +534,7 @@ TEST_F(RPCAccountLinesHandlerTest, DefaultParameterTest) "quality_out": 0 } ] - })"; + })JSON"; auto handler = AnyHandler{AccountLinesHandler{this->backend_}}; auto const output = handler.process(input, Context{yield}); @@ -581,10 +581,10 @@ TEST_F(RPCAccountLinesHandlerTest, UseLimit) runSpawn([this](auto yield) { auto handler = AnyHandler{AccountLinesHandler{this->backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 20 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -597,10 +597,10 @@ TEST_F(RPCAccountLinesHandlerTest, UseLimit) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 9 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -610,10 +610,10 @@ TEST_F(RPCAccountLinesHandlerTest, UseLimit) runSpawn([this](auto yield) { auto const handler = AnyHandler{AccountLinesHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 401 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{yield}); @@ -666,11 +666,11 @@ TEST_F(RPCAccountLinesHandlerTest, UseDestination) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": 30, "peer": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT3 )); @@ -703,9 +703,9 @@ TEST_F(RPCAccountLinesHandlerTest, EmptyChannel) .WillByDefault(Return(ownerDir.getSerializer().peekData())); EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -718,7 +718,7 @@ TEST_F(RPCAccountLinesHandlerTest, EmptyChannel) TEST_F(RPCAccountLinesHandlerTest, OptionalResponseFieldWithDeepFreeze) { - static constexpr auto kCORRECT_OUTPUT = R"({ + static constexpr auto kCORRECT_OUTPUT = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, @@ -752,7 +752,7 @@ TEST_F(RPCAccountLinesHandlerTest, OptionalResponseFieldWithDeepFreeze) "deep_freeze": true } ] - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -793,9 +793,9 @@ TEST_F(RPCAccountLinesHandlerTest, OptionalResponseFieldWithDeepFreeze) ON_CALL(*backend_, doFetchLedgerObjects).WillByDefault(Return(bbs)); EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -808,7 +808,7 @@ TEST_F(RPCAccountLinesHandlerTest, OptionalResponseFieldWithDeepFreeze) TEST_F(RPCAccountLinesHandlerTest, FrozenTrustLineResponse) { - static constexpr auto kCORRECT_OUTPUT = R"({ + static constexpr auto kCORRECT_OUTPUT = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, @@ -838,7 +838,7 @@ TEST_F(RPCAccountLinesHandlerTest, FrozenTrustLineResponse) "freeze": true } ] - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader)); @@ -873,9 +873,9 @@ TEST_F(RPCAccountLinesHandlerTest, FrozenTrustLineResponse) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -936,10 +936,10 @@ TEST_F(RPCAccountLinesHandlerTest, MarkerOutput) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": {} - }})", + }})JSON", kACCOUNT, kLIMIT )); @@ -991,11 +991,11 @@ TEST_F(RPCAccountLinesHandlerTest, MarkerInput) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": {}, "marker": "{},{}" - }})", + }})JSON", kACCOUNT, kLIMIT, kINDEX1, @@ -1044,15 +1044,15 @@ TEST_F(RPCAccountLinesHandlerTest, LimitLessThanMin) runSpawn([this](auto yield) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": {} - }})", + }})JSON", kACCOUNT, AccountLinesHandler::kLIMIT_MIN - 1 )); auto const correctOutput = fmt::format( - R"({{ + R"JSON({{ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, @@ -1078,7 +1078,7 @@ TEST_F(RPCAccountLinesHandlerTest, LimitLessThanMin) "quality_out": 0 }} ] - }})", + }})JSON", AccountLinesHandler::kLIMIT_MIN ); @@ -1121,15 +1121,15 @@ TEST_F(RPCAccountLinesHandlerTest, LimitMoreThanMax) runSpawn([this](auto yield) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "limit": {} - }})", + }})JSON", kACCOUNT, AccountLinesHandler::kLIMIT_MAX + 1 )); auto const correctOutput = fmt::format( - R"({{ + R"JSON({{ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, @@ -1155,7 +1155,7 @@ TEST_F(RPCAccountLinesHandlerTest, LimitMoreThanMax) "quality_out": 0 }} ] - }})", + }})JSON", AccountLinesHandler::kLIMIT_MAX ); diff --git a/tests/unit/rpc/handlers/AccountNFTsTests.cpp b/tests/unit/rpc/handlers/AccountNFTsTests.cpp index a7e0af04a..ea9db3d4b 100644 --- a/tests/unit/rpc/handlers/AccountNFTsTests.cpp +++ b/tests/unit/rpc/handlers/AccountNFTsTests.cpp @@ -86,67 +86,67 @@ generateTestValuesForParametersTest() return std::vector{ { .testName = "AccountMissing", - .testJson = R"({})", + .testJson = R"JSON({})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'account' missing", }, { .testName = "AccountNotString", - .testJson = R"({"account": 123})", + .testJson = R"JSON({"account": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountNotString", }, { .testName = "AccountInvalid", - .testJson = R"({"account": "123"})", + .testJson = R"JSON({"account": "123"})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accountMalformed", }, { .testName = "LedgerHashInvalid", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed", }, { .testName = "LedgerHashNotString", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString", }, { .testName = "LedgerIndexNotInt", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed", }, { .testName = "LimitNotInt", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "LimitNegative", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "LimitZero", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "MarkerNotString", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": 123})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "markerNotString", }, { .testName = "MarkerInvalid", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": "12;xxx"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": "12;xxx"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "markerMalformed", }, @@ -182,10 +182,10 @@ TEST_F(RPCAccountNFTsHandlerTest, LedgerNotFoundViaHash) .WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_hash":"{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -208,10 +208,10 @@ TEST_F(RPCAccountNFTsHandlerTest, LedgerNotFoundViaStringIndex) ON_CALL(*backend_, fetchLedgerBySequence(kSEQ, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_index":"{}" - }})", + }})JSON", kACCOUNT, kSEQ )); @@ -234,10 +234,10 @@ TEST_F(RPCAccountNFTsHandlerTest, LedgerNotFoundViaIntIndex) ON_CALL(*backend_, fetchLedgerBySequence(kSEQ, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_index":{} - }})", + }})JSON", kACCOUNT, kSEQ )); @@ -261,9 +261,9 @@ TEST_F(RPCAccountNFTsHandlerTest, AccountNotFound) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountNFTsHandler{backend_}}; @@ -279,7 +279,7 @@ TEST_F(RPCAccountNFTsHandlerTest, AccountNotFound) TEST_F(RPCAccountNFTsHandlerTest, NormalPath) { static auto const kEXPECTED_OUTPUT = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":30, "validated":true, @@ -296,7 +296,7 @@ TEST_F(RPCAccountNFTsHandlerTest, NormalPath) }} ], "limit":100 - }})", + }})JSON", kLEDGER_HASH, kACCOUNT, kTOKEN_ID, @@ -324,9 +324,9 @@ TEST_F(RPCAccountNFTsHandlerTest, NormalPath) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountNFTsHandler{backend_}}; @@ -358,10 +358,10 @@ TEST_F(RPCAccountNFTsHandlerTest, Limit) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1 + kLIMIT); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, kLIMIT )); @@ -393,10 +393,10 @@ TEST_F(RPCAccountNFTsHandlerTest, Marker) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{}" - }})", + }})JSON", kACCOUNT, kPAGE )); @@ -420,10 +420,10 @@ TEST_F(RPCAccountNFTsHandlerTest, InvalidMarker) .WillByDefault(Return(accountObject.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{}" - }})", + }})JSON", kACCOUNT, kINVALID_PAGE )); @@ -449,9 +449,9 @@ TEST_F(RPCAccountNFTsHandlerTest, AccountWithNoNFT) .WillByDefault(Return(accountObject.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountNFTsHandler{backend_}}; @@ -481,10 +481,10 @@ TEST_F(RPCAccountNFTsHandlerTest, invalidPage) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{}" - }})", + }})JSON", kACCOUNT, kPAGE )); @@ -501,7 +501,7 @@ TEST_F(RPCAccountNFTsHandlerTest, invalidPage) TEST_F(RPCAccountNFTsHandlerTest, LimitLessThanMin) { static auto const kEXPECTED_OUTPUT = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":30, "validated":true, @@ -518,7 +518,7 @@ TEST_F(RPCAccountNFTsHandlerTest, LimitLessThanMin) }} ], "limit":{} - }})", + }})JSON", kLEDGER_HASH, kACCOUNT, kTOKEN_ID, @@ -547,10 +547,10 @@ TEST_F(RPCAccountNFTsHandlerTest, LimitLessThanMin) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, AccountNFTsHandler::kLIMIT_MIN - 1 )); @@ -565,7 +565,7 @@ TEST_F(RPCAccountNFTsHandlerTest, LimitLessThanMin) TEST_F(RPCAccountNFTsHandlerTest, LimitMoreThanMax) { static auto const kEXPECTED_OUTPUT = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":30, "validated":true, @@ -582,7 +582,7 @@ TEST_F(RPCAccountNFTsHandlerTest, LimitMoreThanMax) }} ], "limit":{} - }})", + }})JSON", kLEDGER_HASH, kACCOUNT, kTOKEN_ID, @@ -611,10 +611,10 @@ TEST_F(RPCAccountNFTsHandlerTest, LimitMoreThanMax) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, AccountNFTsHandler::kLIMIT_MAX + 1 )); diff --git a/tests/unit/rpc/handlers/AccountObjectsTests.cpp b/tests/unit/rpc/handlers/AccountObjectsTests.cpp index 86a057760..a9b0cd398 100644 --- a/tests/unit/rpc/handlers/AccountObjectsTests.cpp +++ b/tests/unit/rpc/handlers/AccountObjectsTests.cpp @@ -90,92 +90,92 @@ generateTestValuesForParametersTest() return std::vector{ AccountObjectsParamTestCaseBundle{ .testName = "MissingAccount", - .testJson = R"({})", + .testJson = R"JSON({})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'account' missing" }, AccountObjectsParamTestCaseBundle{ .testName = "AccountNotString", - .testJson = R"({"account":1})", + .testJson = R"JSON({"account":1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountNotString" }, AccountObjectsParamTestCaseBundle{ .testName = "AccountInvalid", - .testJson = R"({"account":"xxx"})", + .testJson = R"JSON({"account":"xxx"})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accountMalformed" }, AccountObjectsParamTestCaseBundle{ .testName = "TypeNotString", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "type":1})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "type":1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountObjectsParamTestCaseBundle{ .testName = "TypeInvalid", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "type":"wrong"})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "type":"wrong"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid field 'type'." }, AccountObjectsParamTestCaseBundle{ .testName = "TypeNotAccountOwned", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "type":"amendments"})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "type":"amendments"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid field 'type'." }, AccountObjectsParamTestCaseBundle{ .testName = "LedgerHashInvalid", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":"1"})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":"1"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed" }, AccountObjectsParamTestCaseBundle{ .testName = "LedgerHashNotString", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":1})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString" }, AccountObjectsParamTestCaseBundle{ .testName = "LedgerIndexInvalid", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_index":"a"})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_index":"a"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, AccountObjectsParamTestCaseBundle{ .testName = "LimitNotInt", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "limit":"1"})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "limit":"1"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountObjectsParamTestCaseBundle{ .testName = "LimitNegative", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "limit":-1})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "limit":-1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountObjectsParamTestCaseBundle{ .testName = "LimitZero", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "limit":0})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "limit":0})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountObjectsParamTestCaseBundle{ .testName = "MarkerNotString", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "marker":9})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "marker":9})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "markerNotString" }, AccountObjectsParamTestCaseBundle{ .testName = "MarkerInvalid", - .testJson = R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "marker":"xxxx"})", + .testJson = R"JSON({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "marker":"xxxx"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Malformed cursor." }, AccountObjectsParamTestCaseBundle{ .testName = "NFTMarkerInvalid", .testJson = fmt::format( - R"({{"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "marker":"wronghex256,{}"}})", + R"JSON({{"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "marker":"wronghex256,{}"}})JSON", std::numeric_limits::max() ), .expectedError = "invalidParams", @@ -183,13 +183,14 @@ generateTestValuesForParametersTest() }, AccountObjectsParamTestCaseBundle{ .testName = "DeletionBlockersOnlyInvalidString", - .testJson = R"({"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "deletion_blockers_only": "wrong"})", + .testJson = + R"JSON({"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "deletion_blockers_only": "wrong"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountObjectsParamTestCaseBundle{ .testName = "DeletionBlockersOnlyInvalidNull", - .testJson = R"({"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "deletion_blockers_only": null})", + .testJson = R"JSON({"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "deletion_blockers_only": null})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, @@ -223,10 +224,10 @@ TEST_F(RPCAccountObjectsHandlerTest, LedgerNonExistViaIntSequence) EXPECT_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ, _)).WillOnce(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_index":30 - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountObjectsHandler{backend_}}; @@ -245,10 +246,10 @@ TEST_F(RPCAccountObjectsHandlerTest, LedgerNonExistViaStringSequence) EXPECT_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ, _)).WillOnce(Return(std::nullopt)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_index":"30" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountObjectsHandler{backend_}}; @@ -268,10 +269,10 @@ TEST_F(RPCAccountObjectsHandlerTest, LedgerNonExistViaHash) .WillOnce(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_hash":"{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -293,9 +294,9 @@ TEST_F(RPCAccountObjectsHandlerTest, AccountNotExist) EXPECT_CALL(*backend_, doFetchLedgerObject).WillOnce(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountObjectsHandler{backend_}}; @@ -310,7 +311,7 @@ TEST_F(RPCAccountObjectsHandlerTest, AccountNotExist) TEST_F(RPCAccountObjectsHandlerTest, DefaultParameterNoNFTFound) { - static constexpr auto kEXPECTED_OUT = R"({ + static constexpr auto kEXPECTED_OUT = R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -340,7 +341,7 @@ TEST_F(RPCAccountObjectsHandlerTest, DefaultParameterNoNFTFound) "index":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" } ] - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ); EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader)); @@ -365,9 +366,9 @@ TEST_F(RPCAccountObjectsHandlerTest, DefaultParameterNoNFTFound) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); @@ -409,10 +410,10 @@ TEST_F(RPCAccountObjectsHandlerTest, Limit) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, kLIMIT )); @@ -453,10 +454,10 @@ TEST_F(RPCAccountObjectsHandlerTest, Marker) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{},{}" - }})", + }})JSON", kACCOUNT, kINDEX1, kPAGE @@ -507,10 +508,10 @@ TEST_F(RPCAccountObjectsHandlerTest, MultipleDirNoNFT) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, 2 * kCOUNT )); @@ -561,10 +562,10 @@ TEST_F(RPCAccountObjectsHandlerTest, TypeFilter) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "type":"offer" - }})", + }})JSON", kACCOUNT )); @@ -605,10 +606,10 @@ TEST_F(RPCAccountObjectsHandlerTest, TypeFilterAmmType) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "type": "amm" - }})", + }})JSON", kACCOUNT )); @@ -658,10 +659,10 @@ TEST_F(RPCAccountObjectsHandlerTest, TypeFilterReturnEmpty) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "type": "check" - }})", + }})JSON", kACCOUNT )); @@ -713,10 +714,10 @@ TEST_F(RPCAccountObjectsHandlerTest, DeletionBlockersOnlyFilter) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "deletion_blockers_only": true - }})", + }})JSON", kACCOUNT )); @@ -756,11 +757,11 @@ TEST_F(RPCAccountObjectsHandlerTest, DeletionBlockersOnlyFilterWithTypeFilter) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "deletion_blockers_only": true, "type": "payment_channel" - }})", + }})JSON", kACCOUNT )); @@ -818,10 +819,10 @@ TEST_F(RPCAccountObjectsHandlerTest, DeletionBlockersOnlyFilterEmptyResult) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "deletion_blockers_only": true - }})", + }})JSON", kACCOUNT )); @@ -878,11 +879,11 @@ TEST_F(RPCAccountObjectsHandlerTest, DeletionBlockersOnlyFilterWithIncompatibleT EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "deletion_blockers_only": true, "type": "offer" - }})", + }})JSON", kACCOUNT )); @@ -896,7 +897,7 @@ TEST_F(RPCAccountObjectsHandlerTest, DeletionBlockersOnlyFilterWithIncompatibleT TEST_F(RPCAccountObjectsHandlerTest, NFTMixOtherObjects) { - static constexpr auto kEXPECTED_OUT = R"({ + static constexpr auto kEXPECTED_OUT = R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -957,7 +958,7 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTMixOtherObjects) "index":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" } ] - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ); EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader)); @@ -992,9 +993,9 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTMixOtherObjects) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); @@ -1031,10 +1032,10 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTReachLimitReturnMarker) } auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, 10 )); @@ -1080,10 +1081,10 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTReachLimitNoMarker) EXPECT_CALL(*backend_, doFetchLedgerObject(current, 30, _)).WillOnce(Return(nftpage11.getSerializer().peekData())); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, 11 )); @@ -1158,10 +1159,10 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTMarker) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{},{}" - }})", + }})JSON", kACCOUNT, ripple::strHex(marker), std::numeric_limits::max() @@ -1214,10 +1215,10 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTMarkerNoMoreNFT) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{},{}" - }})", + }})JSON", kACCOUNT, ripple::strHex(ripple::uint256{beast::zero}), std::numeric_limits::max() @@ -1242,10 +1243,10 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTMarkerNotInRange) EXPECT_CALL(*backend_, doFetchLedgerObject(accountKk, kMAX_SEQ, _)).WillOnce(Return(Blob{'f', 'a', 'k', 'e'})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "marker" : "{},{}" - }})", + }})JSON", kACCOUNT, kINDEX1, std::numeric_limits::max() @@ -1275,10 +1276,10 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTMarkerNotExist) EXPECT_CALL(*backend_, doFetchLedgerObject(accountNftMax, kMAX_SEQ, _)).WillOnce(Return(std::nullopt)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "marker" : "{},{}" - }})", + }})JSON", kACCOUNT, ripple::strHex(accountNftMax), std::numeric_limits::max() @@ -1349,11 +1350,11 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTLimitAdjust) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{},{}", "limit": 12 - }})", + }})JSON", kACCOUNT, ripple::strHex(marker), std::numeric_limits::max() @@ -1371,7 +1372,7 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTLimitAdjust) TEST_F(RPCAccountObjectsHandlerTest, FilterNFT) { - static constexpr auto kEXPECTED_OUT = R"({ + static constexpr auto kEXPECTED_OUT = R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -1410,7 +1411,7 @@ TEST_F(RPCAccountObjectsHandlerTest, FilterNFT) "index":"4B4E9C06F24296074F7BC48F92A97916C6DC5EA9659B25014D08E1BC983515BC" } ] - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ); EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader)); @@ -1445,10 +1446,10 @@ TEST_F(RPCAccountObjectsHandlerTest, FilterNFT) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "type": "nft_page" - }})", + }})JSON", kACCOUNT )); @@ -1486,11 +1487,11 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTZeroMarkerNotAffectOtherMarker) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{}, "marker": "{},{}" - }})", + }})JSON", kACCOUNT, kLIMIT, ripple::strHex(ripple::uint256{beast::zero}), @@ -1509,7 +1510,7 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTZeroMarkerNotAffectOtherMarker) TEST_F(RPCAccountObjectsHandlerTest, LimitLessThanMin) { static auto const kEXPECTED_OUT = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -1539,7 +1540,7 @@ TEST_F(RPCAccountObjectsHandlerTest, LimitLessThanMin) "index":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" }} ] - }})", + }})JSON", AccountObjectsHandler::kLIMIT_MIN ); @@ -1566,10 +1567,10 @@ TEST_F(RPCAccountObjectsHandlerTest, LimitLessThanMin) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit": {} - }})", + }})JSON", kACCOUNT, AccountObjectsHandler::kLIMIT_MIN - 1 )); @@ -1585,7 +1586,7 @@ TEST_F(RPCAccountObjectsHandlerTest, LimitLessThanMin) TEST_F(RPCAccountObjectsHandlerTest, LimitMoreThanMax) { static auto const kEXPECTED_OUT = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -1615,7 +1616,7 @@ TEST_F(RPCAccountObjectsHandlerTest, LimitMoreThanMax) "index":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" }} ] - }})", + }})JSON", AccountObjectsHandler::kLIMIT_MAX ); @@ -1642,10 +1643,10 @@ TEST_F(RPCAccountObjectsHandlerTest, LimitMoreThanMax) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit": {} - }})", + }})JSON", kACCOUNT, AccountObjectsHandler::kLIMIT_MAX + 1 )); @@ -1684,10 +1685,10 @@ TEST_F(RPCAccountObjectsHandlerTest, TypeFilterMPTIssuanceType) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "type": "mpt_issuance" - }})", + }})JSON", kACCOUNT )); @@ -1733,10 +1734,10 @@ TEST_F(RPCAccountObjectsHandlerTest, TypeFilterMPTokenType) EXPECT_CALL(*backend_, doFetchLedgerObjects).WillOnce(Return(bbs)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "type": "mptoken" - }})", + }})JSON", kACCOUNT )); diff --git a/tests/unit/rpc/handlers/AccountOffersTests.cpp b/tests/unit/rpc/handlers/AccountOffersTests.cpp index 3a13bf0a5..8dbb267d4 100644 --- a/tests/unit/rpc/handlers/AccountOffersTests.cpp +++ b/tests/unit/rpc/handlers/AccountOffersTests.cpp @@ -81,67 +81,67 @@ generateTestValuesForParametersTest() return std::vector{ { .testName = "AccountMissing", - .testJson = R"({})", + .testJson = R"JSON({})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'account' missing", }, { .testName = "AccountNotString", - .testJson = R"({"account": 123})", + .testJson = R"JSON({"account": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountNotString", }, { .testName = "AccountInvalid", - .testJson = R"({"account": "123"})", + .testJson = R"JSON({"account": "123"})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accountMalformed", }, { .testName = "LedgerHashInvalid", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed", }, { .testName = "LedgerHashNotString", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString", }, { .testName = "LedgerIndexNotInt", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed", }, { .testName = "LimitNotInt", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "LimitNegative", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "LimitZero", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "MarkerNotString", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": 123})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "markerNotString", }, { .testName = "MarkerInvalid", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": "12;xxx"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": "12;xxx"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Malformed cursor.", }, @@ -177,10 +177,10 @@ TEST_F(RPCAccountOffersHandlerTest, LedgerNotFoundViaHash) .WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_hash":"{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -203,10 +203,10 @@ TEST_F(RPCAccountOffersHandlerTest, LedgerNotFoundViaStringIndex) ON_CALL(*backend_, fetchLedgerBySequence(kSEQ, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_index":"{}" - }})", + }})JSON", kACCOUNT, kSEQ )); @@ -229,10 +229,10 @@ TEST_F(RPCAccountOffersHandlerTest, LedgerNotFoundViaIntIndex) ON_CALL(*backend_, fetchLedgerBySequence(kSEQ, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "ledger_index":{} - }})", + }})JSON", kACCOUNT, kSEQ )); @@ -256,9 +256,9 @@ TEST_F(RPCAccountOffersHandlerTest, AccountNotFound) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountOffersHandler{backend_}}; @@ -274,7 +274,7 @@ TEST_F(RPCAccountOffersHandlerTest, AccountNotFound) TEST_F(RPCAccountOffersHandlerTest, DefaultParams) { auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":30, "validated":true, @@ -295,7 +295,7 @@ TEST_F(RPCAccountOffersHandlerTest, DefaultParams) "expiration":123 }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT, kACCOUNT2 @@ -333,9 +333,9 @@ TEST_F(RPCAccountOffersHandlerTest, DefaultParams) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}" - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountOffersHandler{backend_}}; @@ -381,10 +381,10 @@ TEST_F(RPCAccountOffersHandlerTest, Limit) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":10 - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{AccountOffersHandler{backend_}}; @@ -434,10 +434,10 @@ TEST_F(RPCAccountOffersHandlerTest, Marker) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{},{}" - }})", + }})JSON", kACCOUNT, kINDEX1, startPage @@ -470,10 +470,10 @@ TEST_F(RPCAccountOffersHandlerTest, MarkerNotExists) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "marker":"{},{}" - }})", + }})JSON", kACCOUNT, kINDEX1, startPage @@ -528,10 +528,10 @@ TEST_F(RPCAccountOffersHandlerTest, LimitLessThanMin) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, AccountOffersHandler::kLIMIT_MIN - 1 )); @@ -583,10 +583,10 @@ TEST_F(RPCAccountOffersHandlerTest, LimitMoreThanMax) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account":"{}", "limit":{} - }})", + }})JSON", kACCOUNT, AccountOffersHandler::kLIMIT_MAX + 1 )); diff --git a/tests/unit/rpc/handlers/AccountTxTests.cpp b/tests/unit/rpc/handlers/AccountTxTests.cpp index 4d2f4f90f..a4314d1ee 100644 --- a/tests/unit/rpc/handlers/AccountTxTests.cpp +++ b/tests/unit/rpc/handlers/AccountTxTests.cpp @@ -82,270 +82,270 @@ struct AccountTxParameterTest : public RPCAccountTxHandlerTest, return std::vector{ AccountTxParamTestCaseBundle{ .testName = "MissingAccount", - .testJson = R"({})", + .testJson = R"JSON({})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'account' missing" }, AccountTxParamTestCaseBundle{ .testName = "BinaryNotBool", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "binary": 1})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "binary": 1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "BinaryNotBool_API_v1", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "binary": 1})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "binary": 1})JSON", .expectedError = std::nullopt, .expectedErrorMessage = std::nullopt, .apiVersion = 1u }, AccountTxParamTestCaseBundle{ .testName = "ForwardNotBool", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "forward": 1})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "forward": 1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "ForwardNotBool_API_v1", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "forward": 1})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "forward": 1})JSON", .expectedError = std::nullopt, .expectedErrorMessage = std::nullopt, .apiVersion = 1u }, AccountTxParamTestCaseBundle{ .testName = "ledger_index_minNotInt", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "ledger_index_maxNotInt", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "ledger_indexInvalid", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, AccountTxParamTestCaseBundle{ .testName = "ledger_hashInvalid", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed" }, AccountTxParamTestCaseBundle{ .testName = "ledger_hashNotString", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString" }, AccountTxParamTestCaseBundle{ .testName = "limitNotInt", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "123"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "123"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "limitNegative", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "limitZero", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "MarkerNotObject", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": 101})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": 101})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "invalidMarker" }, AccountTxParamTestCaseBundle{ .testName = "MarkerMissingSeq", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": {"ledger": 123} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'seq' missing" }, AccountTxParamTestCaseBundle{ .testName = "MarkerMissingLedger", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": {"seq": 123} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'ledger' missing" }, AccountTxParamTestCaseBundle{ .testName = "MarkerLedgerNotInt", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": { "seq": "string", "ledger": 1 } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "MarkerSeqNotInt", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": { "ledger": "string", "seq": 1 } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMinLessThanMinSeq", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": 9 - })", + })JSON", .expectedError = "lgrIdxMalformed", .expectedErrorMessage = "ledgerSeqMinOutOfRange" }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxLargeThanMaxSeq", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 31 - })", + })JSON", .expectedError = "lgrIdxMalformed", .expectedErrorMessage = "ledgerSeqMaxOutOfRange" }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxLargeThanMaxSeq_API_v1", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 31 - })", + })JSON", .expectedError = std::nullopt, .expectedErrorMessage = std::nullopt, .apiVersion = 1u }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxSmallerThanMinSeq", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 9 - })", + })JSON", .expectedError = "lgrIdxMalformed", .expectedErrorMessage = "ledgerSeqMaxOutOfRange" }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxSmallerThanMinSeq_API_v1", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 9 - })", + })JSON", .expectedError = "lgrIdxsInvalid", .expectedErrorMessage = "Ledger indexes invalid.", .apiVersion = 1u }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMinSmallerThanMinSeq", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": 9 - })", + })JSON", .expectedError = "lgrIdxMalformed", .expectedErrorMessage = "ledgerSeqMinOutOfRange" }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMinSmallerThanMinSeq_API_v1", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": 9 - })", + })JSON", .expectedError = std::nullopt, .expectedErrorMessage = std::nullopt, .apiVersion = 1u }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMinLargerThanMaxSeq", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": 31 - })", + })JSON", .expectedError = "lgrIdxMalformed", .expectedErrorMessage = "ledgerSeqMinOutOfRange" }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMinLargerThanMaxSeq_API_v1", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": 31 - })", + })JSON", .expectedError = "lgrIdxsInvalid", .expectedErrorMessage = "Ledger indexes invalid.", .apiVersion = 1u }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxLessThanLedgerIndexMin", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 11, "ledger_index_min": 20 - })", + })JSON", .expectedError = "invalidLgrRange", .expectedErrorMessage = "Ledger range is invalid." }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxLessThanLedgerIndexMin_API_v1", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 11, "ledger_index_min": 20 - })", + })JSON", .expectedError = "lgrIdxsInvalid", .expectedErrorMessage = "Ledger indexes invalid.", .apiVersion = 1u }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxMinAndLedgerIndex", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 20, "ledger_index_min": 11, "ledger_index": 10 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "containsLedgerSpecifierAndRange" }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxMinAndLedgerIndexValidated", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 20, "ledger_index_min": 11, "ledger_index": "validated" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "containsLedgerSpecifierAndRange" }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxMinAndLedgerIndex_API_v1", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 20, "ledger_index_min": 11, "ledger_index": 10 - })", + })JSON", .expectedError = std::nullopt, .expectedErrorMessage = std::nullopt, .apiVersion = 1u @@ -353,12 +353,12 @@ struct AccountTxParameterTest : public RPCAccountTxHandlerTest, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxMinAndLedgerHash", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 20, "ledger_index_min": 11, "ledger_hash": "{}" - }})", + }})JSON", kLEDGER_HASH ), .expectedError = "invalidParams", @@ -367,12 +367,12 @@ struct AccountTxParameterTest : public RPCAccountTxHandlerTest, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxMinAndLedgerHash_API_v1", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 20, "ledger_index_min": 11, "ledger_hash": "{}" - }})", + }})JSON", kLEDGER_HASH ), .expectedError = std::nullopt, @@ -381,22 +381,22 @@ struct AccountTxParameterTest : public RPCAccountTxHandlerTest, }, AccountTxParamTestCaseBundle{ .testName = "LedgerIndexMaxMinAndLedgerIndexValidated_API_v1", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": 20, "ledger_index_min": 11, "ledger_index": "validated" - })", + })JSON", .expectedError = std::nullopt, .expectedErrorMessage = std::nullopt, .apiVersion = 1u }, AccountTxParamTestCaseBundle{ .testName = "InvalidTxType", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "tx_type": "unknown" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid field 'tx_type'." } @@ -513,12 +513,12 @@ TEST_F(RPCAccountTxHandlerTest, IndexSpecificForwardTrue) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": true - }})", + }})JSON", kACCOUNT, kMIN_SEQ + 1, kMAX_SEQ - 1 @@ -528,7 +528,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexSpecificForwardTrue) EXPECT_EQ(output.result->at("account").as_string(), kACCOUNT); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ + 1); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ - 1); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger": 12, "seq": 34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_FALSE(output.result->as_object().contains("limit")); }); @@ -555,12 +555,12 @@ TEST_F(RPCAccountTxHandlerTest, IndexSpecificForwardFalse) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kACCOUNT, kMIN_SEQ + 1, kMAX_SEQ - 1 @@ -570,7 +570,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexSpecificForwardFalse) EXPECT_EQ(output.result->at("account").as_string(), kACCOUNT); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ + 1); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ - 1); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger": 12, "seq": 34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_FALSE(output.result->as_object().contains("limit")); }); @@ -597,12 +597,12 @@ TEST_F(RPCAccountTxHandlerTest, IndexNotSpecificForwardTrue) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": true - }})", + }})JSON", kACCOUNT, -1, -1 @@ -612,7 +612,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexNotSpecificForwardTrue) EXPECT_EQ(output.result->at("account").as_string(), kACCOUNT); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger": 12, "seq": 34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_FALSE(output.result->as_object().contains("limit")); }); @@ -639,12 +639,12 @@ TEST_F(RPCAccountTxHandlerTest, IndexNotSpecificForwardFalse) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kACCOUNT, -1, -1 @@ -654,7 +654,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexNotSpecificForwardFalse) EXPECT_EQ(output.result->at("account").as_string(), kACCOUNT); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger": 12, "seq": 34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_FALSE(output.result->as_object().contains("limit")); }); @@ -679,12 +679,12 @@ TEST_F(RPCAccountTxHandlerTest, BinaryTrue) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "binary": true - }})", + }})JSON", kACCOUNT, -1, -1 @@ -694,7 +694,7 @@ TEST_F(RPCAccountTxHandlerTest, BinaryTrue) EXPECT_EQ(output.result->at("account").as_string(), kACCOUNT); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger": 12, "seq": 34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_EQ( output.result->at("transactions").as_array()[0].as_object().at("meta").as_string(), @@ -733,12 +733,12 @@ TEST_F(RPCAccountTxHandlerTest, BinaryTrueV2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "binary": true - }})", + }})JSON", kACCOUNT, -1, -1 @@ -748,7 +748,7 @@ TEST_F(RPCAccountTxHandlerTest, BinaryTrueV2) EXPECT_EQ(output.result->at("account").as_string(), kACCOUNT); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger": 12, "seq": 34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_EQ( output.result->at("transactions").as_array()[0].as_object().at("meta_blob").as_string(), @@ -785,14 +785,14 @@ TEST_F(RPCAccountTxHandlerTest, LimitAndMarker) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "limit": 2, "forward": false, "marker": {{"ledger":10,"seq":11}} - }})", + }})JSON", kACCOUNT, -1, -1 @@ -803,7 +803,7 @@ TEST_F(RPCAccountTxHandlerTest, LimitAndMarker) EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); EXPECT_EQ(output.result->at("limit").as_uint64(), 2); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger": 12, "seq": 34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); }); } @@ -819,13 +819,13 @@ TEST_F(RPCAccountTxHandlerTest, LimitIsCapped) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "limit": 100000, "forward": false - }})", + }})JSON", kACCOUNT, -1, -1 @@ -851,13 +851,13 @@ TEST_F(RPCAccountTxHandlerTest, LimitAllowedUpToCap) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "limit": {}, "forward": false - }})", + }})JSON", kACCOUNT, -1, -1, @@ -899,10 +899,10 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerIndex) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": {} - }})", + }})JSON", kACCOUNT, kMAX_SEQ - 1 )); @@ -925,10 +925,10 @@ TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerIntIndex) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": {} - }})", + }})JSON", kACCOUNT, kMAX_SEQ - 1 )); @@ -948,10 +948,10 @@ TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerStringIndex) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "{}" - }})", + }})JSON", kACCOUNT, kMAX_SEQ - 1 )); @@ -990,10 +990,10 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerHash) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -1034,10 +1034,10 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerIndexValidated) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "validated" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(kINPUT, Context{yield}); @@ -1072,12 +1072,12 @@ TEST_F(RPCAccountTxHandlerTest, TxLessThanMinSeq) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kACCOUNT, kMIN_SEQ + 2, kMAX_SEQ - 1 @@ -1114,12 +1114,12 @@ TEST_F(RPCAccountTxHandlerTest, TxLargerThanMaxSeq) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kACCOUNT, kMIN_SEQ + 1, kMAX_SEQ - 2 @@ -1131,13 +1131,13 @@ TEST_F(RPCAccountTxHandlerTest, TxLargerThanMaxSeq) EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ - 2); EXPECT_EQ(output.result->at("transactions").as_array().size(), 1); EXPECT_FALSE(output.result->as_object().contains("limit")); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger": 12, "seq": 34})JSON")); }); } TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v1) { - auto const out = R"({ + auto const out = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": 10, "ledger_index_max": 30, @@ -1339,7 +1339,7 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v1) "ledger": 12, "seq": 34 } - })"; + })JSON"; auto const transactions = genNFTTransactions(kMIN_SEQ + 1); auto const transCursor = TransactionsAndCursor{.txns = transactions, .cursor = TransactionsCursor{12, 34}}; @@ -1356,13 +1356,13 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v1) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false, "marker": {{"ledger": 10, "seq": 11}} - }})", + }})JSON", kACCOUNT, -1, -1 @@ -1375,7 +1375,7 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v1) TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v2) { - auto const out = R"({ + auto const out = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": 10, "ledger_index_max": 30, @@ -1584,7 +1584,7 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v2) "ledger": 12, "seq": 34 } - })"; + })JSON"; auto const transactions = genNFTTransactions(kMIN_SEQ + 1); auto const transCursor = TransactionsAndCursor{.txns = transactions, .cursor = TransactionsCursor{12, 34}}; @@ -1604,13 +1604,13 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false, "marker": {{"ledger": 10, "seq": 11}} - }})", + }})JSON", kACCOUNT, -1, -1 @@ -1638,228 +1638,228 @@ generateTransactionTypeTestValues() return std::vector{ AccountTxTransactionBundle{ .testName = "DIDSet", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "DIDSet" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "DIDDelete", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "DIDDelete" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "AccountSet", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "AccountSet" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "AccountDelete", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "AccountDelete" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "AMMBid", - .testJson = R"({ + .testJson = R"JSON({ "account": "rLNaPoKeeBjZe2qs6x52yVPZpZ8td4dc6w", "ledger_index": "validated", "tx_type": "AMMBid" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "AMMCreate", - .testJson = R"({ + .testJson = R"JSON({ "account": "rLNaPoKeeBjZe2qs6x52yVPZpZ8td4dc6w", "ledger_index": "validated", "tx_type": "AMMCreate" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "AMMDelete", - .testJson = R"({ + .testJson = R"JSON({ "account": "rLNaPoKeeBjZe2qs6x52yVPZpZ8td4dc6w", "ledger_index": "validated", "tx_type": "AMMDelete" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "AMMDeposit", - .testJson = R"({ + .testJson = R"JSON({ "account": "rLNaPoKeeBjZe2qs6x52yVPZpZ8td4dc6w", "ledger_index": "validated", "tx_type": "AMMDeposit" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "AMMVote", - .testJson = R"({ + .testJson = R"JSON({ "account": "rLNaPoKeeBjZe2qs6x52yVPZpZ8td4dc6w", "ledger_index": "validated", "tx_type": "AMMVote" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "CheckCancel", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "CheckCancel" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "CheckCash", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "CheckCash" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "CheckCreate", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "CheckCreate" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "Clawback", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "Clawback" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "DepositPreauth", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "DepositPreauth" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "EscrowCancel", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "EscrowCancel" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "EscrowCreate", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "EscrowCreate" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "EscrowFinish", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "EscrowFinish" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "NFTokenAcceptOffer", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "NFTokenAcceptOffer" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "NFTokenBurn", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "NFTokenBurn" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "NFTokenCancelOffer", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "NFTokenCancelOffer" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "NFTokenCreateOffer", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "NFTokenCreateOffer" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "NFTokenMint", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "NFTokenMint" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "OfferCancel", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "OfferCancel" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "OfferCreate", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "OfferCreate" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "Payment_API_v1", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "Payment" - })", - .result = R"([ + })JSON", + .result = R"JSON([ { "meta": { "AffectedNodes": [ @@ -1902,17 +1902,17 @@ generateTransactionTypeTestValues() }, "validated": true } - ])", + ])JSON", .apiVersion = 1u }, AccountTxTransactionBundle{ .testName = "Lowercase_Payment", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "payment" - })", - .result = R"([ + })JSON", + .result = R"JSON([ { "meta": { "AffectedNodes": [ @@ -1955,17 +1955,17 @@ generateTransactionTypeTestValues() }, "validated": true } - ])", + ])JSON", .apiVersion = 1u }, AccountTxTransactionBundle{ .testName = "Payment_API_v2", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "Payment" - })", - .result = R"([ + })JSON", + .result = R"JSON([ { "hash": "51D2AAA6B8E4E16EF22F6424854283D8391B56875858A711B8CE4D5B9A422CC2", "ledger_index": 30, @@ -2009,96 +2009,96 @@ generateTransactionTypeTestValues() }, "validated": true } - ])", + ])JSON", .apiVersion = 2u }, AccountTxTransactionBundle{ .testName = "FilterWhenBinaryTrue", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "Payment", "binary": true - })", - .result = R"([{ + })JSON", + .result = R"JSON([{ "meta": "201C00000000F8E5110061E762400000000000001681144B4E9C06F24296074F7BC48F92A97916C6DC5EA9E1E1E5110061E76240000000000000178114D31252CF902EF8DD8451243869B38667CBD89DF3E1E1F1031000", "tx_blob": "120000240000002061400000000000000168400000000000000173047465737481144B4E9C06F24296074F7BC48F92A97916C6DC5EA98314D31252CF902EF8DD8451243869B38667CBD89DF3", "ledger_index": 30, "validated": true - }])", + }])JSON", .apiVersion = 1u }, AccountTxTransactionBundle{ .testName = "PaymentChannelClaim", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "PaymentChannelClaim", "binary": true - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "FilterWhenBinaryTrueEmptyResult", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "PaymentChannelClaim" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "PaymentChannelCreate", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "PaymentChannelCreate" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "PaymentChannelFund", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "PaymentChannelFund" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "SetRegularKey", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "SetRegularKey" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "SignerListSet", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "SignerListSet" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "TicketCreate", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "TicketCreate" - })", + })JSON", .result = "[]" }, AccountTxTransactionBundle{ .testName = "TrustSet", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "validated", "tx_type": "TrustSet" - })", + })JSON", .result = "[]" }, }; diff --git a/tests/unit/rpc/handlers/BookChangesTests.cpp b/tests/unit/rpc/handlers/BookChangesTests.cpp index a74f1480d..8006d2b84 100644 --- a/tests/unit/rpc/handlers/BookChangesTests.cpp +++ b/tests/unit/rpc/handlers/BookChangesTests.cpp @@ -79,19 +79,19 @@ generateTestValuesForParametersTest() return std::vector{ BookChangesParamTestCaseBundle{ .testName = "LedgerHashInvalid", - .testJson = R"({"ledger_hash":"1"})", + .testJson = R"JSON({"ledger_hash":"1"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed" }, BookChangesParamTestCaseBundle{ .testName = "LedgerHashNotString", - .testJson = R"({"ledger_hash":1})", + .testJson = R"JSON({"ledger_hash":1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString" }, BookChangesParamTestCaseBundle{ .testName = "LedgerIndexInvalid", - .testJson = R"({"ledger_index":"a"})", + .testJson = R"JSON({"ledger_index":"a"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, @@ -125,7 +125,7 @@ TEST_F(RPCBookChangesHandlerTest, LedgerNonExistViaIntSequence) // return empty ledgerHeader ON_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ, _)).WillByDefault(Return(std::optional{})); - auto static const kINPUT = json::parse(R"({"ledger_index":30})"); + auto static const kINPUT = json::parse(R"JSON({"ledger_index":30})JSON"); auto const handler = AnyHandler{BookChangesHandler{backend_}}; runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); @@ -142,7 +142,7 @@ TEST_F(RPCBookChangesHandlerTest, LedgerNonExistViaStringSequence) // return empty ledgerHeader ON_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ, _)).WillByDefault(Return(std::nullopt)); - auto static const kINPUT = json::parse(R"({"ledger_index":"30"})"); + auto static const kINPUT = json::parse(R"JSON({"ledger_index":"30"})JSON"); auto const handler = AnyHandler{BookChangesHandler{backend_}}; runSpawn([&](auto yield) { auto const output = handler.process(kINPUT, Context{yield}); @@ -161,9 +161,9 @@ TEST_F(RPCBookChangesHandlerTest, LedgerNonExistViaHash) .WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}" - }})", + }})JSON", kLEDGER_HASH )); auto const handler = AnyHandler{BookChangesHandler{backend_}}; @@ -179,7 +179,7 @@ TEST_F(RPCBookChangesHandlerTest, LedgerNonExistViaHash) TEST_F(RPCBookChangesHandlerTest, NormalPath) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "type":"bookChanges", "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, @@ -197,7 +197,7 @@ TEST_F(RPCBookChangesHandlerTest, NormalPath) "close":"-1" } ] - })"; + })JSON"; EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); ON_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ, _)) diff --git a/tests/unit/rpc/handlers/BookOffersTests.cpp b/tests/unit/rpc/handlers/BookOffersTests.cpp index 2f52f2d29..a00ec0516 100644 --- a/tests/unit/rpc/handlers/BookOffersTests.cpp +++ b/tests/unit/rpc/handlers/BookOffersTests.cpp @@ -115,79 +115,79 @@ generateParameterBookOffersTestBundles() return std::vector{ ParameterTestBundle{ .testName = "MissingTakerGets", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "USD", "issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'taker_gets' missing" }, ParameterTestBundle{ .testName = "MissingTakerPays", - .testJson = R"({ + .testJson = R"JSON({ "taker_gets" : { "currency" : "USD", "issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'taker_pays' missing" }, ParameterTestBundle{ .testName = "WrongTypeTakerPays", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : "wrong", "taker_gets" : { "currency" : "XRP" } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParameterTestBundle{ .testName = "WrongTypeTakerGets", - .testJson = R"({ + .testJson = R"JSON({ "taker_gets" : "wrong", "taker_pays" : { "currency" : "XRP" } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParameterTestBundle{ .testName = "TakerPaysMissingCurrency", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : {}, "taker_gets" : { "currency" : "XRP" } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'currency' missing" }, ParameterTestBundle{ .testName = "TakerGetsMissingCurrency", - .testJson = R"({ + .testJson = R"JSON({ "taker_gets" : {}, "taker_pays" : { "currency" : "XRP" } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'currency' missing" }, ParameterTestBundle{ .testName = "TakerGetsWrongCurrency", - .testJson = R"({ + .testJson = R"JSON({ "taker_gets" : { "currency" : "CNYY", @@ -197,13 +197,13 @@ generateParameterBookOffersTestBundles() { "currency" : "XRP" } - })", + })JSON", .expectedError = "dstAmtMalformed", .expectedErrorMessage = "Destination amount/currency/issuer is malformed." }, ParameterTestBundle{ .testName = "TakerPaysWrongCurrency", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNYY", @@ -213,13 +213,13 @@ generateParameterBookOffersTestBundles() { "currency" : "XRP" } - })", + })JSON", .expectedError = "srcCurMalformed", .expectedErrorMessage = "Source currency is malformed." }, ParameterTestBundle{ .testName = "TakerGetsCurrencyNotString", - .testJson = R"({ + .testJson = R"JSON({ "taker_gets" : { "currency" : 123, @@ -229,13 +229,13 @@ generateParameterBookOffersTestBundles() { "currency" : "XRP" } - })", + })JSON", .expectedError = "dstAmtMalformed", .expectedErrorMessage = "Destination amount/currency/issuer is malformed." }, ParameterTestBundle{ .testName = "TakerPaysCurrencyNotString", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : 123, @@ -245,13 +245,13 @@ generateParameterBookOffersTestBundles() { "currency" : "XRP" } - })", + })JSON", .expectedError = "srcCurMalformed", .expectedErrorMessage = "Source currency is malformed." }, ParameterTestBundle{ .testName = "TakerGetsWrongIssuer", - .testJson = R"({ + .testJson = R"JSON({ "taker_gets" : { "currency" : "CNY", @@ -261,13 +261,13 @@ generateParameterBookOffersTestBundles() { "currency" : "XRP" } - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Destination issuer is malformed." }, ParameterTestBundle{ .testName = "TakerPaysWrongIssuer", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -277,13 +277,13 @@ generateParameterBookOffersTestBundles() { "currency" : "XRP" } - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Source issuer is malformed." }, ParameterTestBundle{ .testName = "InvalidTaker", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -294,13 +294,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP" }, "taker": "123" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid field 'taker'." }, ParameterTestBundle{ .testName = "TakerNotString", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -311,13 +311,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP" }, "taker": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid field 'taker'." }, ParameterTestBundle{ .testName = "LimitNotInt", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -328,13 +328,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP" }, "limit": "123" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParameterTestBundle{ .testName = "LimitNegative", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -345,13 +345,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP" }, "limit": -1 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParameterTestBundle{ .testName = "LimitZero", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -362,13 +362,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP" }, "limit": 0 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParameterTestBundle{ .testName = "LedgerIndexInvalid", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -379,13 +379,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP" }, "ledger_index": "xxx" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, ParameterTestBundle{ .testName = "LedgerHashInvalid", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -396,13 +396,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP" }, "ledger_hash": "xxx" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed" }, ParameterTestBundle{ .testName = "LedgerHashNotString", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -413,13 +413,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP" }, "ledger_hash": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString" }, ParameterTestBundle{ .testName = "GetsPaysXRPWithIssuer", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "XRP", @@ -430,13 +430,13 @@ generateParameterBookOffersTestBundles() "currency" : "CNY", "issuer" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" } - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Unneeded field 'taker_pays.issuer' for XRP currency specification." }, ParameterTestBundle{ .testName = "PaysCurrencyWithXRPIssuer", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "JPY" @@ -446,13 +446,13 @@ generateParameterBookOffersTestBundles() "currency" : "CNY", "issuer" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" } - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Invalid field 'taker_pays.issuer', expected non-XRP issuer." }, ParameterTestBundle{ .testName = "GetsCurrencyWithXRPIssuer", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "XRP" @@ -461,13 +461,13 @@ generateParameterBookOffersTestBundles() { "currency" : "CNY" } - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Invalid field 'taker_gets.issuer', expected non-XRP issuer." }, ParameterTestBundle{ .testName = "GetsXRPWithIssuer", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -478,13 +478,13 @@ generateParameterBookOffersTestBundles() "currency" : "XRP", "issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" } - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Unneeded field 'taker_gets.issuer' for XRP currency specification." }, ParameterTestBundle{ .testName = "BadMarket", - .testJson = R"({ + .testJson = R"JSON({ "taker_pays" : { "currency" : "CNY", @@ -495,7 +495,7 @@ generateParameterBookOffersTestBundles() "currency" : "CNY", "issuer" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" } - })", + })JSON", .expectedError = "badMarket", .expectedErrorMessage = "badMarket" } @@ -619,7 +619,7 @@ generateNormalPathBookOffersTestBundles() )); auto const getsXRPPaysUSDInputJson = fmt::format( - R"({{ + R"JSON({{ "taker_gets": {{ "currency": "XRP" @@ -629,12 +629,12 @@ generateNormalPathBookOffersTestBundles() "currency": "USD", "issuer": "{}" }} - }})", + }})JSON", kACCOUNT ); auto const paysXRPGetsUSDInputJson = fmt::format( - R"({{ + R"JSON({{ "taker_pays": {{ "currency": "XRP" @@ -644,7 +644,7 @@ generateNormalPathBookOffersTestBundles() "currency": "USD", "issuer": "{}" }} - }})", + }})JSON", kACCOUNT ); @@ -685,7 +685,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 5, .mockedOffers = std::vector{gets10XRPPays20USDOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers":[ @@ -710,7 +710,7 @@ generateNormalPathBookOffersTestBundles() "quality":"{}" }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, 193, @@ -745,7 +745,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 5, .mockedOffers = std::vector{gets10XRPPays20USDOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -777,7 +777,7 @@ generateNormalPathBookOffersTestBundles() }} }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, 5, @@ -807,7 +807,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 3, .mockedOffers = std::vector{gets10XRPPays20USDOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -839,7 +839,7 @@ generateNormalPathBookOffersTestBundles() }} }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, 0, @@ -871,7 +871,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 3, .mockedOffers = std::vector{gets10USDPays20XRPOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -903,7 +903,7 @@ generateNormalPathBookOffersTestBundles() }} }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, kPAYS20_XRP_GETS10_USD_BOOK_DIR, @@ -937,7 +937,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 6, .mockedOffers = std::vector{gets10USDPays20XRPOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -969,7 +969,7 @@ generateNormalPathBookOffersTestBundles() "taker_pays_funded":"8" }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, kPAYS20_XRP_GETS10_USD_BOOK_DIR, @@ -1012,7 +1012,7 @@ generateNormalPathBookOffersTestBundles() gets10USDPays20XRPOffer }, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -1063,7 +1063,7 @@ generateNormalPathBookOffersTestBundles() "quality":"{}" }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, kPAYS20_XRP_GETS10_USD_BOOK_DIR, @@ -1098,7 +1098,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 3, .mockedOffers = std::vector{gets10USDPays20XRPOwnerOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -1124,7 +1124,7 @@ generateNormalPathBookOffersTestBundles() "quality":"{}" }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT, kPAYS20_XRP_GETS10_USD_BOOK_DIR, @@ -1158,7 +1158,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 6, .mockedOffers = std::vector{gets10USDPays20XRPOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -1190,7 +1190,7 @@ generateNormalPathBookOffersTestBundles() "taker_pays_funded":"0" }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, kPAYS20_XRP_GETS10_USD_BOOK_DIR, @@ -1223,7 +1223,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 4, .mockedOffers = std::vector{gets10USDPays20XRPOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -1255,7 +1255,7 @@ generateNormalPathBookOffersTestBundles() "taker_pays_funded":"0" }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, kPAYS20_XRP_GETS10_USD_BOOK_DIR, @@ -1291,7 +1291,7 @@ generateNormalPathBookOffersTestBundles() .ledgerObjectCalls = 6, .mockedOffers = std::vector{gets10USDPays20XRPOffer}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"{}", "ledger_index":300, "offers": @@ -1323,7 +1323,7 @@ generateNormalPathBookOffersTestBundles() "taker_pays_funded":"0" }} ] - }})", + }})JSON", kLEDGER_HASH, kACCOUNT2, kPAYS20_XRP_GETS10_USD_BOOK_DIR, @@ -1349,7 +1349,7 @@ TEST_F(RPCBookOffersHandlerTest, LedgerNonExistViaIntSequence) ON_CALL(*backend_, fetchLedgerBySequence(30, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": 30, "taker_gets": {{ @@ -1360,7 +1360,7 @@ TEST_F(RPCBookOffersHandlerTest, LedgerNonExistViaIntSequence) "currency": "USD", "issuer": "{}" }} - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{BookOffersHandler{backend_, mockAmendmentCenterPtr_}}; @@ -1380,7 +1380,7 @@ TEST_F(RPCBookOffersHandlerTest, LedgerNonExistViaSequence) ON_CALL(*backend_, fetchLedgerBySequence(30, _)).WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": "30", "taker_gets": {{ @@ -1391,7 +1391,7 @@ TEST_F(RPCBookOffersHandlerTest, LedgerNonExistViaSequence) "currency": "USD", "issuer": "{}" }} - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{BookOffersHandler{backend_, mockAmendmentCenterPtr_}}; @@ -1412,7 +1412,7 @@ TEST_F(RPCBookOffersHandlerTest, LedgerNonExistViaHash) .WillByDefault(Return(std::optional{})); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_hash": "{}", "taker_gets": {{ @@ -1423,7 +1423,7 @@ TEST_F(RPCBookOffersHandlerTest, LedgerNonExistViaHash) "currency": "USD", "issuer": "{}" }} - }})", + }})JSON", kLEDGER_HASH, kACCOUNT )); @@ -1488,7 +1488,7 @@ TEST_F(RPCBookOffersHandlerTest, Limit) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "taker_gets": {{ "currency": "XRP" @@ -1499,7 +1499,7 @@ TEST_F(RPCBookOffersHandlerTest, Limit) "issuer": "{}" }}, "limit": 5 - }})", + }})JSON", kACCOUNT )); auto const handler = AnyHandler{BookOffersHandler{backend_, mockAmendmentCenterPtr_}}; @@ -1561,7 +1561,7 @@ TEST_F(RPCBookOffersHandlerTest, LimitMoreThanMax) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "taker_gets": {{ "currency": "XRP" @@ -1572,7 +1572,7 @@ TEST_F(RPCBookOffersHandlerTest, LimitMoreThanMax) "issuer": "{}" }}, "limit": {} - }})", + }})JSON", kACCOUNT, BookOffersHandler::kLIMIT_MAX + 1 )); diff --git a/tests/unit/rpc/handlers/DefaultProcessorTests.cpp b/tests/unit/rpc/handlers/DefaultProcessorTests.cpp index f7c9c8cb3..73b2095bf 100644 --- a/tests/unit/rpc/handlers/DefaultProcessorTests.cpp +++ b/tests/unit/rpc/handlers/DefaultProcessorTests.cpp @@ -45,7 +45,7 @@ TEST_F(RPCDefaultProcessorTest, ValidInput) HandlerMock const handler; rpc::impl::DefaultProcessor const processor; - auto const input = json::parse(R"({ "something": "works" })"); + auto const input = json::parse(R"JSON({ "something": "works" })JSON"); auto const spec = RpcSpec{{"something", Required{}}}; auto const data = InOutFake{"works"}; EXPECT_CALL(handler, spec(_)).WillOnce(ReturnRef(spec)); @@ -64,7 +64,7 @@ TEST_F(RPCDefaultProcessorTest, NoInputValidCall) rpc::impl::DefaultProcessor const processor; auto const data = InOutFake{"works"}; - auto const input = json::parse(R"({})"); + auto const input = json::parse(R"JSON({})JSON"); EXPECT_CALL(handler, process(_)).WillOnce(Return(data)); auto const ret = processor(handler, input, Context{yield}); @@ -79,7 +79,7 @@ TEST_F(RPCDefaultProcessorTest, InvalidInput) HandlerMock const handler; rpc::impl::DefaultProcessor const processor; - auto const input = json::parse(R"({ "other": "nope" })"); + auto const input = json::parse(R"JSON({ "other": "nope" })JSON"); auto const spec = RpcSpec{{"something", Required{}}}; EXPECT_CALL(handler, spec(_)).WillOnce(ReturnRef(spec)); diff --git a/tests/unit/rpc/handlers/DepositAuthorizedTests.cpp b/tests/unit/rpc/handlers/DepositAuthorizedTests.cpp index 8d21e0e3d..179cb7582 100644 --- a/tests/unit/rpc/handlers/DepositAuthorizedTests.cpp +++ b/tests/unit/rpc/handlers/DepositAuthorizedTests.cpp @@ -89,121 +89,121 @@ generateTestValuesForParametersTest() return std::vector{ { .testName = "SourceAccountMissing", - .testJson = R"({ + .testJson = R"JSON({ "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'source_account' missing", }, { .testName = "SourceAccountMalformed", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - })", + })JSON", .expectedError = "actMalformed", .expectedErrorMessage = "source_accountMalformed", }, { .testName = "SourceAccountNotString", - .testJson = R"({ + .testJson = R"JSON({ "source_account": 1234, "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "source_accountNotString", }, { .testName = "DestinationAccountMissing", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'destination_account' missing", }, { .testName = "DestinationAccountMalformed", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - })", + })JSON", .expectedError = "actMalformed", .expectedErrorMessage = "destination_accountMalformed", }, { .testName = "DestinationAccountNotString", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": 1234, "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "destination_accountNotString", }, { .testName = "LedgerHashInvalid", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed", }, { .testName = "LedgerHashNotString", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString", }, { .testName = "LedgerIndexNotInt", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed", }, { .testName = "CredentialsNotArray", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "credentials": "x" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "CredentialsNotStringsInArray", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "credentials": [123] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Item is not a valid uint256 type.", }, { .testName = "CredentialsNotHexedStringInArray", - .testJson = R"({ + .testJson = R"JSON({ "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "credentials": ["234", "432"] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Item is not a valid uint256 type.", } @@ -241,11 +241,11 @@ TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaIntSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{DepositAuthorizedHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_index": {} - }})", + }})JSON", kACCOUNT, kACCOUNT2, kRANGE_MAX @@ -268,11 +268,11 @@ TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaStringSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{DepositAuthorizedHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_index": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT2, kRANGE_MAX @@ -295,11 +295,11 @@ TEST_F(RPCDepositAuthorizedTest, LedgerNotExistViaHash) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{DepositAuthorizedHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH @@ -325,11 +325,11 @@ TEST_F(RPCDepositAuthorizedTest, SourceAccountDoesNotExist) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH @@ -362,11 +362,11 @@ TEST_F(RPCDepositAuthorizedTest, DestinationAccountDoesNotExist) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH @@ -387,14 +387,14 @@ TEST_F(RPCDepositAuthorizedTest, DestinationAccountDoesNotExist) TEST_F(RPCDepositAuthorizedTest, AccountsAreEqual) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, "deposit_authorized": true, "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); @@ -406,11 +406,11 @@ TEST_F(RPCDepositAuthorizedTest, AccountsAreEqual) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT, kLEDGER_HASH @@ -428,14 +428,14 @@ TEST_F(RPCDepositAuthorizedTest, AccountsAreEqual) TEST_F(RPCDepositAuthorizedTest, DifferentAccountsNoDepositAuthFlag) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, "deposit_authorized": true, "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun" - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); @@ -452,11 +452,11 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsNoDepositAuthFlag) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH @@ -474,14 +474,14 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsNoDepositAuthFlag) TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsFalse) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, "deposit_authorized": false, "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun" - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); @@ -499,11 +499,11 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsFals EXPECT_CALL(*backend_, doFetchLedgerObject).Times(3); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH @@ -521,14 +521,14 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsFals TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsTrue) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, "deposit_authorized": true, "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun" - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); @@ -546,11 +546,11 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsTrue EXPECT_CALL(*backend_, doFetchLedgerObject).Times(3); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH @@ -568,7 +568,7 @@ TEST_F(RPCDepositAuthorizedTest, DifferentAccountsWithDepositAuthFlagReturnsTrue TEST_F(RPCDepositAuthorizedTest, CredentialAcceptedAndNotExpiredReturnsTrue) { static auto const kEXPECTED_OUT = fmt::format( - R"({{ + R"JSON({{ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, @@ -576,7 +576,7 @@ TEST_F(RPCDepositAuthorizedTest, CredentialAcceptedAndNotExpiredReturnsTrue) "source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "destination_account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "credentials": ["{}"] - }})", + }})JSON", kCREDENTIAL_HASH // CREDENTIALHASH should match credentialIndex ); @@ -604,12 +604,12 @@ TEST_F(RPCDepositAuthorizedTest, CredentialAcceptedAndNotExpiredReturnsTrue) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(4); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}", "credentials": ["{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH, @@ -652,12 +652,12 @@ TEST_F(RPCDepositAuthorizedTest, CredentialNotAuthorizedReturnsFalse) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(3); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}", "credentials": ["{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH, @@ -708,12 +708,12 @@ TEST_F(RPCDepositAuthorizedTest, CredentialExpiredReturnsFalse) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(3); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}", "credentials": ["{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH, @@ -758,12 +758,12 @@ TEST_F(RPCDepositAuthorizedTest, DuplicateCredentialsReturnsFalse) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(3); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}", "credentials": ["{}", "{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH, @@ -800,12 +800,12 @@ TEST_F(RPCDepositAuthorizedTest, NoElementsInCredentialsReturnsFalse) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(2); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}", "credentials": [] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH @@ -851,12 +851,12 @@ TEST_F(RPCDepositAuthorizedTest, MoreThanMaxNumberOfCredentialsReturnsFalse) std::vector credentials(9, ripple::strHex(credentialIndex)); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}", "credentials": [{}] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH, @@ -905,12 +905,12 @@ TEST_F(RPCDepositAuthorizedTest, DifferentSubjectAccountForCredentialReturnsFals EXPECT_CALL(*backend_, doFetchLedgerObject).Times(3); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "source_account": "{}", "destination_account": "{}", "ledger_hash": "{}", "credentials": ["{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kLEDGER_HASH, diff --git a/tests/unit/rpc/handlers/FeatureTests.cpp b/tests/unit/rpc/handlers/FeatureTests.cpp index 18e25a242..f2c190d43 100644 --- a/tests/unit/rpc/handlers/FeatureTests.cpp +++ b/tests/unit/rpc/handlers/FeatureTests.cpp @@ -78,75 +78,75 @@ generateTestValuesForParametersTest() // Note: on rippled this and below returns "badFeature" RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeFeatureBool", - .testJson = R"({"feature": true})", + .testJson = R"JSON({"feature": true})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeFeatureInt", - .testJson = R"({"feature": 42})", + .testJson = R"JSON({"feature": 42})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeFeatureDouble", - .testJson = R"({"feature": 4.2})", + .testJson = R"JSON({"feature": 4.2})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeFeatureNull", - .testJson = R"({"feature": null})", + .testJson = R"JSON({"feature": null})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, // Note: this and below internal errors on rippled RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeFeatureObj", - .testJson = R"({"feature": {}})", + .testJson = R"JSON({"feature": {}})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeFeatureArray", - .testJson = R"({"feature": []})", + .testJson = R"JSON({"feature": []})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, // "vetoed" should always be blocked RPCFeatureHandlerParamTestCaseBundle{ .testName = "VetoedPassed", - .testJson = R"({"feature": "foo", "vetoed": true})", + .testJson = R"JSON({"feature": "foo", "vetoed": true})JSON", .expectedError = "noPermission", .expectedErrorMessage = "The admin portion of feature API is not available through Clio." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeVetoedString", - .testJson = R"({"feature": "foo", "vetoed": "test"})", + .testJson = R"JSON({"feature": "foo", "vetoed": "test"})JSON", .expectedError = "noPermission", .expectedErrorMessage = "The admin portion of feature API is not available through Clio." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeVetoedInt", - .testJson = R"({"feature": "foo", "vetoed": 42})", + .testJson = R"JSON({"feature": "foo", "vetoed": 42})JSON", .expectedError = "noPermission", .expectedErrorMessage = "The admin portion of feature API is not available through Clio." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeVetoedDouble", - .testJson = R"({"feature": "foo", "vetoed": 4.2})", + .testJson = R"JSON({"feature": "foo", "vetoed": 4.2})JSON", .expectedError = "noPermission", .expectedErrorMessage = "The admin portion of feature API is not available through Clio." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeVetoedObject", - .testJson = R"({"feature": "foo", "vetoed": {}})", + .testJson = R"JSON({"feature": "foo", "vetoed": {}})JSON", .expectedError = "noPermission", .expectedErrorMessage = "The admin portion of feature API is not available through Clio." }, RPCFeatureHandlerParamTestCaseBundle{ .testName = "InvalidTypeVetoedArray", - .testJson = R"({"feature": "foo", "vetoed": []})", + .testJson = R"JSON({"feature": "foo", "vetoed": []})JSON", .expectedError = "noPermission", .expectedErrorMessage = "The admin portion of feature API is not available through Clio." }, @@ -182,9 +182,9 @@ TEST_F(RPCFeatureHandlerTest, LedgerNotExistViaIntSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{FeatureHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = boost::json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": {} - }})", + }})JSON", kRANGE_MAX )); auto const output = handler.process(req, Context{yield}); @@ -202,9 +202,9 @@ TEST_F(RPCFeatureHandlerTest, LedgerNotExistViaStringSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{FeatureHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = boost::json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": "{}" - }})", + }})JSON", kRANGE_MAX )); auto const output = handler.process(req, Context{yield}); @@ -223,9 +223,9 @@ TEST_F(RPCFeatureHandlerTest, LedgerNotExistViaHash) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{FeatureHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = boost::json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_hash": "{}" - }})", + }})JSON", kLEDGER_HASH )); auto const output = handler.process(req, Context{yield}); @@ -241,7 +241,7 @@ TEST_F(RPCFeatureHandlerTest, AlwaysNoPermissionForVetoed) runSpawn([this](auto yield) { auto const handler = AnyHandler{FeatureHandler{backend_, mockAmendmentCenterPtr_}}; auto const output = - handler.process(boost::json::parse(R"({"vetoed": true, "feature": "foo"})"), Context{yield}); + handler.process(boost::json::parse(R"JSON({"vetoed": true, "feature": "foo"})JSON"), Context{yield}); ASSERT_FALSE(output); @@ -279,7 +279,7 @@ TEST_F(RPCFeatureHandlerTest, SuccessPathViaNameWithSingleSupportedAndEnabledRes EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(testing::Return(ledgerHeader)); auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "2E2FB9CF8A44EB80F4694D38AADAE9B8B7ADAFD2F092E10068E61C98C4F092B0": {{ "name": "fixUniversalNumber", @@ -289,14 +289,15 @@ TEST_F(RPCFeatureHandlerTest, SuccessPathViaNameWithSingleSupportedAndEnabledRes "ledger_hash": "{}", "ledger_index": {}, "validated": true - }})", + }})JSON", kLEDGER_HASH, kSEQ ); runSpawn([this, &expectedOutput](auto yield) { auto const handler = AnyHandler{FeatureHandler{backend_, mockAmendmentCenterPtr_}}; - auto const output = handler.process(boost::json::parse(R"({"feature": "fixUniversalNumber"})"), Context{yield}); + auto const output = + handler.process(boost::json::parse(R"JSON({"feature": "fixUniversalNumber"})JSON"), Context{yield}); ASSERT_TRUE(output); EXPECT_EQ(*output.result, boost::json::parse(expectedOutput)); @@ -329,7 +330,7 @@ TEST_F(RPCFeatureHandlerTest, SuccessPathViaHashWithSingleResult) EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(testing::Return(ledgerHeader)); auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "2E2FB9CF8A44EB80F4694D38AADAE9B8B7ADAFD2F092E10068E61C98C4F092B0": {{ "name": "fixUniversalNumber", @@ -339,7 +340,7 @@ TEST_F(RPCFeatureHandlerTest, SuccessPathViaHashWithSingleResult) "ledger_hash": "{}", "ledger_index": {}, "validated": true - }})", + }})JSON", kLEDGER_HASH, kSEQ ); @@ -347,7 +348,9 @@ TEST_F(RPCFeatureHandlerTest, SuccessPathViaHashWithSingleResult) runSpawn([this, &expectedOutput](auto yield) { auto const handler = AnyHandler{FeatureHandler{backend_, mockAmendmentCenterPtr_}}; auto const output = handler.process( - boost::json::parse(R"({"feature": "2E2FB9CF8A44EB80F4694D38AADAE9B8B7ADAFD2F092E10068E61C98C4F092B0"})"), + boost::json::parse( + R"JSON({"feature": "2E2FB9CF8A44EB80F4694D38AADAE9B8B7ADAFD2F092E10068E61C98C4F092B0"})JSON" + ), Context{yield} ); @@ -372,7 +375,8 @@ TEST_F(RPCFeatureHandlerTest, BadFeaturePath) runSpawn([this](auto yield) { auto const handler = AnyHandler{FeatureHandler{backend_, mockAmendmentCenterPtr_}}; - auto const output = handler.process(boost::json::parse(R"({"feature": "nonexistent"})"), Context{yield}); + auto const output = + handler.process(boost::json::parse(R"JSON({"feature": "nonexistent"})JSON"), Context{yield}); ASSERT_FALSE(output); @@ -412,7 +416,7 @@ TEST_F(RPCFeatureHandlerTest, SuccessPathWithMultipleResults) createAmendmentsObject({Amendments::fixUniversalNumber, Amendments::fixRemoveNFTokenAutoTrustLine}); auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "features": {{ "2E2FB9CF8A44EB80F4694D38AADAE9B8B7ADAFD2F092E10068E61C98C4F092B0": {{ @@ -430,14 +434,14 @@ TEST_F(RPCFeatureHandlerTest, SuccessPathWithMultipleResults) "ledger_hash": "{}", "ledger_index": {}, "validated": true - }})", + }})JSON", kLEDGER_HASH, kSEQ ); runSpawn([this, &expectedOutput](auto yield) { auto const handler = AnyHandler{FeatureHandler{backend_, mockAmendmentCenterPtr_}}; - auto const output = handler.process(boost::json::parse(R"({})"), Context{yield}); + auto const output = handler.process(boost::json::parse(R"JSON({})JSON"), Context{yield}); ASSERT_TRUE(output); EXPECT_EQ(*output.result, boost::json::parse(expectedOutput)); diff --git a/tests/unit/rpc/handlers/GatewayBalancesTests.cpp b/tests/unit/rpc/handlers/GatewayBalancesTests.cpp index 0368c1b4e..c5eaba04a 100644 --- a/tests/unit/rpc/handlers/GatewayBalancesTests.cpp +++ b/tests/unit/rpc/handlers/GatewayBalancesTests.cpp @@ -99,34 +99,34 @@ generateParameterTestBundles() return std::vector{ ParameterTestBundle{ .testName = "AccountNotString", - .testJson = R"({ + .testJson = R"JSON({ "account": 1213 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountNotString" }, ParameterTestBundle{ .testName = "AccountMissing", - .testJson = R"({ - })", + .testJson = R"JSON({ + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'account' missing" }, ParameterTestBundle{ .testName = "AccountInvalid", - .testJson = R"({ + .testJson = R"JSON({ "account": "1213" - })", + })JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accountMalformed" }, ParameterTestBundle{ .testName = "LedgerIndexInvalid", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "meh" - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -135,10 +135,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "LedgerHashInvalid", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "meh" - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -147,10 +147,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "LedgerHashNotString", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": 12 - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -159,10 +159,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "WalletsNotStringOrArrayV1", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "hotwallet": 12 - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidHotWallet", @@ -171,10 +171,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "WalletsNotStringAccountV1", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "hotwallet": [12] - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidHotWallet", @@ -183,10 +183,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "WalletsInvalidAccountV1", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "hotwallet": ["12"] - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidHotWallet", @@ -195,10 +195,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "WalletInvalidAccountV1", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "hotwallet": "12" - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidHotWallet", @@ -207,10 +207,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "WalletsNotStringOrArrayV2", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "hotwallet": 12 - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -220,10 +220,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "WalletsNotStringAccountV2", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "hotwallet": [12] - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -233,10 +233,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "WalletsInvalidAccountV2", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "hotwallet": ["12"] - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -246,10 +246,10 @@ generateParameterTestBundles() ParameterTestBundle{ .testName = "WalletInvalidAccountV2", .testJson = fmt::format( - R"({{ + R"JSON({{ "account": "{}", "hotwallet": "12" - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -278,10 +278,10 @@ TEST_F(RPCGatewayBalancesHandlerTest, LedgerNotFoundViaStringIndex) runSpawn([&](auto yield) { auto const output = handler.process( json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": "{}" - }})", + }})JSON", kACCOUNT, seq )), @@ -306,10 +306,10 @@ TEST_F(RPCGatewayBalancesHandlerTest, LedgerNotFoundViaIntIndex) runSpawn([&](auto yield) { auto const output = handler.process( json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_index": {} - }})", + }})JSON", kACCOUNT, seq )), @@ -333,10 +333,10 @@ TEST_F(RPCGatewayBalancesHandlerTest, LedgerNotFoundViaHash) runSpawn([&](auto yield) { auto const output = handler.process( json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )), @@ -367,9 +367,9 @@ TEST_F(RPCGatewayBalancesHandlerTest, AccountNotFound) runSpawn([&](auto yield) { auto const output = handler.process( json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}" - }})", + }})JSON", kACCOUNT )), Context{yield} @@ -426,10 +426,10 @@ TEST_P(NormalPathTest, CheckOutput) runSpawn([&](auto yield) { auto const output = handler.process( json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", {} - }})", + }})JSON", kACCOUNT, bundle.hotwallet )), @@ -477,7 +477,7 @@ generateNormalPathTestBundles() }, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "obligations":{{ "JPY":"50" }}, @@ -516,7 +516,7 @@ generateNormalPathTestBundles() "account":"{}", "ledger_index":300, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - }})", + }})JSON", kACCOUNT2, kACCOUNT3, kACCOUNT3, @@ -531,14 +531,14 @@ generateNormalPathTestBundles() "JPY", kISSUER, -50, kACCOUNT, 10, kACCOUNT3, 20, kTXN_ID, 123 )}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "obligations":{{ "JPY":"50" }}, "account":"{}", "ledger_index":300, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - }})", + }})JSON", kACCOUNT ), .hotwallet = R"("ledger_index" : "validated")" @@ -548,14 +548,14 @@ generateNormalPathTestBundles() .mockedDir = createOwnerDirLedgerObject({ripple::uint256{kINDEX2}, ripple::uint256{kINDEX2}}, kINDEX1), .mockedObjects = std::vector{overflowState, overflowState}, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "obligations":{{ "JPY":"9999999999999999e80" }}, "account":"{}", "ledger_index":300, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - }})", + }})JSON", kACCOUNT ), .hotwallet = R"("ledger_index" : "validated")" @@ -577,7 +577,7 @@ generateNormalPathTestBundles() createRippleStateLedgerObject("JPY", kISSUER, -50, kACCOUNT3, 10, kACCOUNT, 20, kTXN_ID, 123) }, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "obligations":{{ "EUR":"30" }}, @@ -604,7 +604,7 @@ generateNormalPathTestBundles() "account":"{}", "ledger_index":300, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - }})", + }})JSON", kACCOUNT2, kACCOUNT3, kACCOUNT @@ -624,7 +624,7 @@ generateNormalPathTestBundles() }, .expectedJson = fmt::format( - R"({{ + R"JSON({{ "balances":{{ "{}":[ {{ @@ -646,7 +646,7 @@ generateNormalPathTestBundles() "account":"{}", "ledger_index":300, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - }})", + }})JSON", kACCOUNT3, kACCOUNT2, kACCOUNT diff --git a/tests/unit/rpc/handlers/GetAggregatePriceTests.cpp b/tests/unit/rpc/handlers/GetAggregatePriceTests.cpp index d422bcd43..dafb1501b 100644 --- a/tests/unit/rpc/handlers/GetAggregatePriceTests.cpp +++ b/tests/unit/rpc/handlers/GetAggregatePriceTests.cpp @@ -112,34 +112,34 @@ generateTestValuesForParametersTest() return std::vector{ GetAggregatePriceParamTestCaseBundle{ .testName = "ledger_indexInvalid", - .testJson = R"({"ledger_index": "x"})", + .testJson = R"JSON({"ledger_index": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, GetAggregatePriceParamTestCaseBundle{ .testName = "ledger_hashInvalid", - .testJson = R"({"ledger_hash": "x"})", + .testJson = R"JSON({"ledger_hash": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed" }, GetAggregatePriceParamTestCaseBundle{ .testName = "ledger_hashNotString", - .testJson = R"({"ledger_hash": 123})", + .testJson = R"JSON({"ledger_hash": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString" }, GetAggregatePriceParamTestCaseBundle{ .testName = "no_oracles", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "XRP", "quote_asset": "USD" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'oracles' missing" }, GetAggregatePriceParamTestCaseBundle{ .testName = "no_base_asset", - .testJson = R"({ + .testJson = R"JSON({ "quote_asset": "USD", "oracles": [ @@ -148,13 +148,13 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'base_asset' missing" }, GetAggregatePriceParamTestCaseBundle{ .testName = "invalid_base_asset", - .testJson = R"({ + .testJson = R"JSON({ "quote_asset" : "USD", "base_asset": "asdf", "oracles": @@ -164,13 +164,13 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "empty_base_asset", - .testJson = R"({ + .testJson = R"JSON({ "quote_asset" : "USD", "base_asset": "", "oracles": @@ -180,13 +180,13 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "invalid_base_asset2", - .testJson = R"({ + .testJson = R"JSON({ "quote_asset" : "USD", "base_asset": "+aa", "oracles": @@ -196,13 +196,13 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "no_quote_asset", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "oracles": [ @@ -211,13 +211,13 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'quote_asset' missing" }, GetAggregatePriceParamTestCaseBundle{ .testName = "invalid_quote_asset", - .testJson = R"({ + .testJson = R"JSON({ "quote_asset" : "asdf", "base_asset": "USD", "oracles": @@ -227,13 +227,13 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "empty_quote_asset", - .testJson = R"({ + .testJson = R"JSON({ "quote_asset" : "", "base_asset": "USD", "oracles": @@ -243,13 +243,13 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "invalid_quote_asset2", - .testJson = R"({ + .testJson = R"JSON({ "quote_asset" : "+aa", "base_asset": "USD", "oracles": @@ -259,33 +259,33 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "oraclesIsEmpty", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": [] - })", + })JSON", .expectedError = "oracleMalformed", .expectedErrorMessage = "Oracle request is malformed." }, GetAggregatePriceParamTestCaseBundle{ .testName = "oraclesNotArray", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": 1 - })", + })JSON", .expectedError = "oracleMalformed", .expectedErrorMessage = "Oracle request is malformed." }, GetAggregatePriceParamTestCaseBundle{ .testName = "thresholdNotInt", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -296,13 +296,13 @@ generateTestValuesForParametersTest() } ], "time_threshold": "x" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "trimNotInt", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -313,13 +313,13 @@ generateTestValuesForParametersTest() } ], "trim": "x" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "trimTooSmall", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -330,13 +330,13 @@ generateTestValuesForParametersTest() } ], "trim": 0 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "trimTooLarge", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -347,13 +347,13 @@ generateTestValuesForParametersTest() } ], "trim": 26 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "oracleAccountInvalid", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -363,13 +363,13 @@ generateTestValuesForParametersTest() "oracle_document_id": 2 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "oracleDocumentIdNotInt", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -379,27 +379,27 @@ generateTestValuesForParametersTest() "oracle_document_id": "a" } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, GetAggregatePriceParamTestCaseBundle{ .testName = "oracleMissingAccount", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": [{"oracle_document_id": 2}] - })", + })JSON", .expectedError = "oracleMalformed", .expectedErrorMessage = "Oracle request is malformed." }, GetAggregatePriceParamTestCaseBundle{ .testName = "oracleMissingDocumentId", - .testJson = R"({ + .testJson = R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": [{"account": "rGh1VZCRBJY6rJiaFpD4LZtyHiuCkC8aeD"}] - })", + })JSON", .expectedError = "oracleMalformed", .expectedErrorMessage = "Oracle request is malformed." }, @@ -430,11 +430,11 @@ TEST_P(GetAggregatePriceParameterTest, InvalidParams) TEST_F(RPCGetAggregatePriceHandlerTest, OverOraclesMax) { auto req = json::parse( - R"({ + R"JSON({ "base_asset": "USD", "quote_asset": "XRP", "oracles": [] - })" + })JSON" ); auto const maxOracles = 200; @@ -458,7 +458,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, LedgerNotFound) EXPECT_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)).WillOnce(Return(std::nullopt)); constexpr auto kDOCUMENT_ID = 1; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -468,7 +468,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, LedgerNotFound) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); @@ -492,7 +492,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntrySinglePriceData) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -502,13 +502,13 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntrySinglePriceData) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "10", @@ -520,7 +520,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntrySinglePriceData) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -541,7 +541,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryStrOracleDocumentId) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -551,13 +551,13 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryStrOracleDocumentId) "oracle_document_id": "{}" }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "10", @@ -569,7 +569,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryStrOracleDocumentId) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -590,7 +590,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, PreviousTxNotFound) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "JPY", "quote_asset": "XRP", "oracles": @@ -600,13 +600,13 @@ TEST_F(RPCGetAggregatePriceHandlerTest, PreviousTxNotFound) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "10", @@ -618,7 +618,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, PreviousTxNotFound) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -655,7 +655,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NewLedgerObjectHasNoPricePair) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "JPY", "quote_asset": "XRP", "oracles": @@ -665,13 +665,13 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NewLedgerObjectHasNoPricePair) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "10", @@ -683,7 +683,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NewLedgerObjectHasNoPricePair) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -711,7 +711,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesOdd) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -729,7 +729,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesOdd) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID1, kACCOUNT, @@ -739,7 +739,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesOdd) )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "110", @@ -751,7 +751,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesOdd) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -779,7 +779,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesEven) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -801,7 +801,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesEven) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID1, kACCOUNT, @@ -813,7 +813,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesEven) )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "92.5", @@ -825,7 +825,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryMultipleOraclesEven) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -853,7 +853,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryTrim) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "trim": {}, @@ -876,7 +876,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryTrim) "oracle_document_id": {} }} ] - }})", + }})JSON", 25, kACCOUNT, kDOCUMENT_ID1, @@ -889,7 +889,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryTrim) )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "92.5", @@ -907,7 +907,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, OracleLedgerEntryTrim) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -929,7 +929,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NoOracleEntryFound) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "oracles": @@ -939,7 +939,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NoOracleEntryFound) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); @@ -963,7 +963,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NoMatchAssetPair) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "JPY", "quote_asset": "XRP", "oracles": @@ -973,7 +973,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NoMatchAssetPair) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); @@ -1007,7 +1007,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIsZero) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "time_threshold": {}, @@ -1030,7 +1030,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIsZero) "oracle_document_id": {} }} ] - }})", + }})JSON", 0, kACCOUNT, kDOCUMENT_ID1, @@ -1043,7 +1043,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIsZero) )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "10", @@ -1055,7 +1055,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIsZero) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kTIMESTAMP1, kRANGE_MAX, kLEDGER_HASH @@ -1087,7 +1087,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, ValidTimeThreshold) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "time_threshold": {}, @@ -1110,7 +1110,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, ValidTimeThreshold) "oracle_document_id": {} }} ] - }})", + }})JSON", kTIMESTAMP1 - kTIMESTAMP2, kACCOUNT, kDOCUMENT_ID1, @@ -1123,7 +1123,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, ValidTimeThreshold) )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "15", @@ -1135,7 +1135,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, ValidTimeThreshold) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kTIMESTAMP1, kRANGE_MAX, kLEDGER_HASH @@ -1167,7 +1167,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdTooLong) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "time_threshold": {}, @@ -1190,7 +1190,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdTooLong) "oracle_document_id": {} }} ] - }})", + }})JSON", kTIMESTAMP1 + 1, kACCOUNT, kDOCUMENT_ID1, @@ -1203,7 +1203,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdTooLong) )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "92.5", @@ -1215,7 +1215,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdTooLong) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -1246,7 +1246,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIncludeOldest) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "USD", "quote_asset": "XRP", "time_threshold": {}, @@ -1269,7 +1269,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIncludeOldest) "oracle_document_id": {} }} ] - }})", + }})JSON", kTIMESTAMP4 - kTIMESTAMP1, kACCOUNT, kDOCUMENT_ID1, @@ -1282,7 +1282,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIncludeOldest) )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "92.5", @@ -1294,7 +1294,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, TimeThresholdIncludeOldest) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -1331,7 +1331,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, FromTx) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "JPY", "quote_asset": "XRP", "oracles": @@ -1341,13 +1341,13 @@ TEST_F(RPCGetAggregatePriceHandlerTest, FromTx) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); auto const expected = json::parse(fmt::format( - R"({{ + R"JSON({{ "entire_set": {{ "mean": "10", @@ -1359,7 +1359,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, FromTx) "ledger_index": {}, "ledger_hash": "{}", "validated": true - }})", + }})JSON", kRANGE_MAX, kLEDGER_HASH )); @@ -1408,7 +1408,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NotFoundInTxHistory) auto const handler = AnyHandler{GetAggregatePriceHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "base_asset": "JPY", "quote_asset": "XRP", "oracles": @@ -1418,7 +1418,7 @@ TEST_F(RPCGetAggregatePriceHandlerTest, NotFoundInTxHistory) "oracle_document_id": {} }} ] - }})", + }})JSON", kACCOUNT, kDOCUMENT_ID )); diff --git a/tests/unit/rpc/handlers/LedgerDataTests.cpp b/tests/unit/rpc/handlers/LedgerDataTests.cpp index 303f1fb64..d02ce7d4a 100644 --- a/tests/unit/rpc/handlers/LedgerDataTests.cpp +++ b/tests/unit/rpc/handlers/LedgerDataTests.cpp @@ -82,76 +82,76 @@ generateTestValuesForParametersTest() return std::vector{ LedgerDataParamTestCaseBundle{ .testName = "ledger_indexInvalid", - .testJson = R"({"ledger_index": "x"})", + .testJson = R"JSON({"ledger_index": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, LedgerDataParamTestCaseBundle{ .testName = "ledger_hashInvalid", - .testJson = R"({"ledger_hash": "x"})", + .testJson = R"JSON({"ledger_hash": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed" }, LedgerDataParamTestCaseBundle{ .testName = "ledger_hashNotString", - .testJson = R"({"ledger_hash": 123})", + .testJson = R"JSON({"ledger_hash": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString" }, LedgerDataParamTestCaseBundle{ .testName = "binaryNotBool", - .testJson = R"({"binary": 123})", + .testJson = R"JSON({"binary": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, LedgerDataParamTestCaseBundle{ .testName = "limitNotInt", - .testJson = R"({"limit": "xxx"})", + .testJson = R"JSON({"limit": "xxx"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, LedgerDataParamTestCaseBundle{ .testName = "limitNegative", - .testJson = R"({"limit": -1})", + .testJson = R"JSON({"limit": -1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, LedgerDataParamTestCaseBundle{ .testName = "limitZero", - .testJson = R"({"limit": 0})", + .testJson = R"JSON({"limit": 0})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, LedgerDataParamTestCaseBundle{ .testName = "markerInvalid", - .testJson = R"({"marker": "xxx"})", + .testJson = R"JSON({"marker": "xxx"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "markerMalformed" }, LedgerDataParamTestCaseBundle{ .testName = "markerOutOfOrder", - .testJson = R"({ + .testJson = R"JSON({ "marker": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "out_of_order": true - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "outOfOrderMarkerNotInt" }, LedgerDataParamTestCaseBundle{ .testName = "markerNotString", - .testJson = R"({"marker": 123})", + .testJson = R"JSON({"marker": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "markerNotString" }, LedgerDataParamTestCaseBundle{ .testName = "typeNotString", - .testJson = R"({"type": 123})", + .testJson = R"JSON({"type": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid field 'type', not string." }, LedgerDataParamTestCaseBundle{ .testName = "typeNotValid", - .testJson = R"({"type": "xxx"})", + .testJson = R"JSON({"type": "xxx"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid field 'type'." }, @@ -187,9 +187,9 @@ TEST_F(RPCLedgerDataHandlerTest, LedgerNotExistViaIntSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": {} - }})", + }})JSON", kRANGE_MAX )); auto const output = handler.process(req, Context{yield}); @@ -208,9 +208,9 @@ TEST_F(RPCLedgerDataHandlerTest, LedgerNotExistViaStringSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": "{}" - }})", + }})JSON", kRANGE_MAX )); auto const output = handler.process(req, Context{yield}); @@ -229,9 +229,9 @@ TEST_F(RPCLedgerDataHandlerTest, LedgerNotExistViaHash) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_hash": "{}" - }})", + }})JSON", kLEDGER_HASH )); auto const output = handler.process(req, Context{yield}); @@ -255,9 +255,9 @@ TEST_F(RPCLedgerDataHandlerTest, MarkerNotExist) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "marker": "{}" - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -270,7 +270,7 @@ TEST_F(RPCLedgerDataHandlerTest, MarkerNotExist) TEST_F(RPCLedgerDataHandlerTest, NoMarker) { - static auto const kLEDGER_EXPECTED = R"({ + static auto const kLEDGER_EXPECTED = R"JSON({ "account_hash":"0000000000000000000000000000000000000000000000000000000000000000", "close_flags":0, "close_time":0, @@ -283,7 +283,7 @@ TEST_F(RPCLedgerDataHandlerTest, NoMarker) "total_coins":"0", "transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000", "closed":true - })"; + })JSON"; EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(createLedgerHeader(kLEDGER_HASH, kRANGE_MAX))); @@ -310,7 +310,7 @@ TEST_F(RPCLedgerDataHandlerTest, NoMarker) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; - auto const req = json::parse(R"({"limit":10})"); + auto const req = json::parse(R"JSON({"limit":10})JSON"); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); EXPECT_TRUE(output.result->as_object().contains("ledger")); @@ -328,7 +328,7 @@ TEST_F(RPCLedgerDataHandlerTest, NoMarker) TEST_F(RPCLedgerDataHandlerTest, Version2) { - static auto const kLEDGER_EXPECTED = R"({ + static auto const kLEDGER_EXPECTED = R"JSON({ "account_hash": "0000000000000000000000000000000000000000000000000000000000000000", "close_flags": 0, "close_time": 0, @@ -341,7 +341,7 @@ TEST_F(RPCLedgerDataHandlerTest, Version2) "total_coins": "0", "transaction_hash": "0000000000000000000000000000000000000000000000000000000000000000", "closed": true - })"; + })JSON"; EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(createLedgerHeader(kLEDGER_HASH, kRANGE_MAX))); @@ -368,7 +368,7 @@ TEST_F(RPCLedgerDataHandlerTest, Version2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; - auto const req = json::parse(R"({"limit":10})"); + auto const req = json::parse(R"JSON({"limit":10})JSON"); auto output = handler.process(req, Context{.yield = yield, .apiVersion = 2}); ASSERT_TRUE(output); EXPECT_TRUE(output.result->as_object().contains("ledger")); @@ -382,7 +382,7 @@ TEST_F(RPCLedgerDataHandlerTest, Version2) TEST_F(RPCLedgerDataHandlerTest, TypeFilter) { - static auto const kLEDGER_EXPECTED = R"({ + static auto const kLEDGER_EXPECTED = R"JSON({ "account_hash":"0000000000000000000000000000000000000000000000000000000000000000", "close_flags":0, "close_time":0, @@ -395,7 +395,7 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilter) "total_coins":"0", "transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000", "closed":true - })"; + })JSON"; EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); ON_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)) @@ -424,10 +424,10 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilter) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; - auto const req = json::parse(R"({ + auto const req = json::parse(R"JSON({ "limit":10, "type":"state" - })"); + })JSON"); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -446,7 +446,7 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilter) TEST_F(RPCLedgerDataHandlerTest, TypeFilterAMM) { - static auto const kLEDGER_EXPECTED = R"({ + static auto const kLEDGER_EXPECTED = R"JSON({ "account_hash":"0000000000000000000000000000000000000000000000000000000000000000", "close_flags":0, "close_time":0, @@ -459,7 +459,7 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilterAMM) "total_coins":"0", "transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000", "closed":true - })"; + })JSON"; EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); ON_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)) @@ -485,10 +485,10 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilterAMM) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; - auto const req = json::parse(R"({ + auto const req = json::parse(R"JSON({ "limit":6, "type":"amm" - })"); + })JSON"); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -507,7 +507,7 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilterAMM) TEST_F(RPCLedgerDataHandlerTest, OutOfOrder) { - static auto const kLEDGER_EXPECTED = R"({ + static auto const kLEDGER_EXPECTED = R"JSON({ "account_hash":"0000000000000000000000000000000000000000000000000000000000000000", "close_flags":0, "close_time":0, @@ -520,7 +520,7 @@ TEST_F(RPCLedgerDataHandlerTest, OutOfOrder) "total_coins":"0", "transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000", "closed":true - })"; + })JSON"; EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); ON_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)) @@ -542,7 +542,7 @@ TEST_F(RPCLedgerDataHandlerTest, OutOfOrder) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; - auto const req = json::parse(R"({"limit":10, "out_of_order":true})"); + auto const req = json::parse(R"JSON({"limit":10, "out_of_order":true})JSON"); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); EXPECT_TRUE(output.result->as_object().contains("ledger")); @@ -589,10 +589,10 @@ TEST_F(RPCLedgerDataHandlerTest, Marker) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "limit":10, "marker": "{}" - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -632,11 +632,11 @@ TEST_F(RPCLedgerDataHandlerTest, DiffMarker) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "limit":10, "marker": {}, "out_of_order": true - }})", + }})JSON", kRANGE_MAX )); auto const output = handler.process(req, Context{yield}); @@ -673,10 +673,10 @@ TEST_F(RPCLedgerDataHandlerTest, Binary) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse( - R"({ + R"JSON({ "limit":10, "binary": true - })" + })JSON" ); auto const output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -713,10 +713,10 @@ TEST_F(RPCLedgerDataHandlerTest, BinaryLimitMoreThanMax) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "limit":{}, "binary": true - }})", + }})JSON", LedgerDataHandler::kLIMIT_BINARY + 1 )); auto const output = handler.process(req, Context{yield}); @@ -754,10 +754,10 @@ TEST_F(RPCLedgerDataHandlerTest, JsonLimitMoreThanMax) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "limit":{}, "binary": false - }})", + }})JSON", LedgerDataHandler::kLIMIT_JSON + 1 )); auto const output = handler.process(req, Context{yield}); @@ -788,10 +788,10 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilterMPTIssuance) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; - auto const req = json::parse(R"({ + auto const req = json::parse(R"JSON({ "limit":1, "type":"mpt_issuance" - })"); + })JSON"); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -830,10 +830,10 @@ TEST_F(RPCLedgerDataHandlerTest, TypeFilterMPToken) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerDataHandler{backend_}}; - auto const req = json::parse(R"({ + auto const req = json::parse(R"JSON({ "limit":1, "type":"mptoken" - })"); + })JSON"); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); diff --git a/tests/unit/rpc/handlers/LedgerEntryTests.cpp b/tests/unit/rpc/handlers/LedgerEntryTests.cpp index 6399c8310..3067b4760 100644 --- a/tests/unit/rpc/handlers/LedgerEntryTests.cpp +++ b/tests/unit/rpc/handlers/LedgerEntryTests.cpp @@ -101,105 +101,105 @@ generateTestValuesForParametersTest() return std::vector{ ParamTestCaseBundle{ .testName = "InvalidBinaryType", - .testJson = R"({ + .testJson = R"JSON({ "index": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD", "binary": "invalid" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParamTestCaseBundle{ .testName = "InvalidAccountRootFormat", - .testJson = R"({ + .testJson = R"JSON({ "account_root": "invalid" - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "InvalidDidFormat", - .testJson = R"({ + .testJson = R"JSON({ "did": "invalid" - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "InvalidAccountRootNotString", - .testJson = R"({ + .testJson = R"JSON({ "account_root": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "account_rootNotString" }, ParamTestCaseBundle{ .testName = "InvalidLedgerIndex", - .testJson = R"({ + .testJson = R"JSON({ "ledger_index": "wrong" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, ParamTestCaseBundle{ .testName = "UnknownOption", - .testJson = R"({})", + .testJson = R"JSON({})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParamTestCaseBundle{ .testName = "InvalidDepositPreauthType", - .testJson = R"({ + .testJson = R"JSON({ "deposit_preauth": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParamTestCaseBundle{ .testName = "InvalidDepositPreauthString", - .testJson = R"({ + .testJson = R"JSON({ "deposit_preauth": "invalid" - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidDepositPreauthEmptyJson", - .testJson = R"({ + .testJson = R"JSON({ "deposit_preauth": {} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'owner' missing" }, ParamTestCaseBundle{ .testName = "InvalidDepositPreauthJsonWrongAccount", - .testJson = R"({ + .testJson = R"JSON({ "deposit_preauth": { "owner": "invalid", "authorized": "invalid" } - })", + })JSON", .expectedError = "malformedOwner", .expectedErrorMessage = "Malformed owner." }, ParamTestCaseBundle{ .testName = "InvalidDepositPreauthJsonOwnerNotString", - .testJson = R"({ + .testJson = R"JSON({ "deposit_preauth": { "owner": 123, "authorized": 123 } - })", + })JSON", .expectedError = "malformedOwner", .expectedErrorMessage = "Malformed owner." }, @@ -207,12 +207,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidDepositPreauthJsonAuthorizedNotString", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized": 123 }} - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -222,12 +222,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidDepositPreauthJsonAuthorizeCredentialsNotArray", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": "asdf" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -237,12 +237,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidDepositPreauthJsonAuthorizeCredentialsMalformedString", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": ["C2F2A19C8D0D893D18F18FDCFE13A3ECB41767E48422DF07F2455CDA08FDF09B"] }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedAuthorizedCredentials", @@ -252,11 +252,11 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthBothAuthAndAuthCredentialsDoesNotExists", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -266,7 +266,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthBothAuthAndAuthCredentialsExists", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized": "{}", @@ -277,7 +277,7 @@ generateTestValuesForParametersTest() }} ] }} - }})", + }})JSON", kACCOUNT, kACCOUNT2, kACCOUNT3, @@ -290,13 +290,13 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthEmptyAuthorizeCredentials", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": [ ] }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedAuthorizedCredentials", @@ -306,7 +306,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthAuthorizeCredentialsMissingCredentialType", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": [ @@ -315,7 +315,7 @@ generateTestValuesForParametersTest() }} ] }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -326,7 +326,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthAuthorizeCredentialsMissingIssuer", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": [ @@ -335,7 +335,7 @@ generateTestValuesForParametersTest() }} ] }} - }})", + }})JSON", kACCOUNT, kCREDENTIAL_TYPE ), @@ -346,7 +346,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthAuthorizeCredentialsIncorrectIssuerType", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": [ @@ -356,7 +356,7 @@ generateTestValuesForParametersTest() }} ] }} - }})", + }})JSON", kACCOUNT, kCREDENTIAL_TYPE ), @@ -367,7 +367,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthAuthorizeCredentialsIncorrectCredentialType", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": [ @@ -377,7 +377,7 @@ generateTestValuesForParametersTest() }} ] }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -388,7 +388,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthAuthorizeCredentialsCredentialTypeNotHex", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": [ @@ -398,7 +398,7 @@ generateTestValuesForParametersTest() }} ] }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -409,7 +409,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthAuthorizeCredentialsCredentialTypeEmpty", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": [ @@ -419,7 +419,7 @@ generateTestValuesForParametersTest() }} ] }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -430,7 +430,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "DepositPreauthDuplicateAuthorizeCredentials", .testJson = fmt::format( - R"({{ + R"JSON({{ "deposit_preauth": {{ "owner": "{}", "authorized_credentials": [ @@ -444,7 +444,7 @@ generateTestValuesForParametersTest() }} ] }} - }})", + }})JSON", kACCOUNT, kACCOUNT2, kCREDENTIAL_TYPE, @@ -457,51 +457,51 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidTicketType", - .testJson = R"({ + .testJson = R"JSON({ "ticket": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParamTestCaseBundle{ .testName = "InvalidTicketIndex", - .testJson = R"({ + .testJson = R"JSON({ "ticket": "invalid" - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidTicketEmptyJson", - .testJson = R"({ + .testJson = R"JSON({ "ticket": {} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'account' missing" }, ParamTestCaseBundle{ .testName = "InvalidTicketJsonAccountNotString", - .testJson = R"({ + .testJson = R"JSON({ "ticket": { "account": 123, "ticket_seq": 123 } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountNotString" }, ParamTestCaseBundle{ .testName = "InvalidTicketJsonAccountInvalid", - .testJson = R"({ + .testJson = R"JSON({ "ticket": { "account": "123", "ticket_seq": 123 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, @@ -509,12 +509,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidTicketJsonSeqNotInt", .testJson = fmt::format( - R"({{ + R"JSON({{ "ticket": {{ "account": "{}", "ticket_seq": "123" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -523,51 +523,51 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidOfferType", - .testJson = R"({ + .testJson = R"JSON({ "offer": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParamTestCaseBundle{ .testName = "InvalidOfferIndex", - .testJson = R"({ + .testJson = R"JSON({ "offer": "invalid" - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidOfferEmptyJson", - .testJson = R"({ + .testJson = R"JSON({ "offer": {} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'account' missing" }, ParamTestCaseBundle{ .testName = "InvalidOfferJsonAccountNotString", - .testJson = R"({ + .testJson = R"JSON({ "ticket": { "account": 123, "seq": 123 } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountNotString" }, ParamTestCaseBundle{ .testName = "InvalidOfferJsonAccountInvalid", - .testJson = R"({ + .testJson = R"JSON({ "ticket": { "account": "123", "seq": 123 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, @@ -575,12 +575,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidOfferJsonSeqNotInt", .testJson = fmt::format( - R"({{ + R"JSON({{ "offer": {{ "account": "{}", "seq": "123" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -589,51 +589,51 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidEscrowType", - .testJson = R"({ + .testJson = R"JSON({ "escrow": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParamTestCaseBundle{ .testName = "InvalidEscrowIndex", - .testJson = R"({ + .testJson = R"JSON({ "escrow": "invalid" - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidEscrowEmptyJson", - .testJson = R"({ + .testJson = R"JSON({ "escrow": {} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'owner' missing" }, ParamTestCaseBundle{ .testName = "InvalidEscrowJsonAccountNotString", - .testJson = R"({ + .testJson = R"JSON({ "escrow": { "owner": 123, "seq": 123 } - })", + })JSON", .expectedError = "malformedOwner", .expectedErrorMessage = "Malformed owner." }, ParamTestCaseBundle{ .testName = "InvalidEscrowJsonAccountInvalid", - .testJson = R"({ + .testJson = R"JSON({ "escrow": { "owner": "123", "seq": 123 } - })", + })JSON", .expectedError = "malformedOwner", .expectedErrorMessage = "Malformed owner." }, @@ -641,12 +641,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidEscrowJsonSeqNotInt", .testJson = fmt::format( - R"({{ + R"JSON({{ "escrow": {{ "owner": "{}", "seq": "123" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -655,29 +655,29 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidRippleStateType", - .testJson = R"({ + .testJson = R"JSON({ "ripple_state": "123" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParamTestCaseBundle{ .testName = "InvalidRippleStateMissField", - .testJson = R"({ + .testJson = R"JSON({ "ripple_state": { "currency": "USD" } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'accounts' missing" }, ParamTestCaseBundle{ .testName = "InvalidRippleStateEmptyJson", - .testJson = R"({ + .testJson = R"JSON({ "ripple_state": {} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'accounts' missing" }, @@ -685,11 +685,11 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidRippleStateOneAccount", .testJson = fmt::format( - R"({{ + R"JSON({{ "ripple_state": {{ "accounts" : ["{}"] }} - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -699,12 +699,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidRippleStateSameAccounts", .testJson = fmt::format( - R"({{ + R"JSON({{ "ripple_state": {{ "accounts" : ["{}","{}"], "currency": "USD" }} - }})", + }})JSON", kACCOUNT, kACCOUNT ), @@ -715,12 +715,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidRippleStateWrongAccountsNotString", .testJson = fmt::format( - R"({{ + R"JSON({{ "ripple_state": {{ "accounts" : ["{}",123], "currency": "USD" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "invalidParams", @@ -730,12 +730,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidRippleStateWrongAccountsFormat", .testJson = fmt::format( - R"({{ + R"JSON({{ "ripple_state": {{ "accounts" : ["{}","123"], "currency": "USD" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedAddress", @@ -745,12 +745,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidRippleStateWrongCurrency", .testJson = fmt::format( - R"({{ + R"JSON({{ "ripple_state": {{ "accounts" : ["{}","{}"], "currency": "XXXX" }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -761,12 +761,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidRippleStateWrongCurrencyNotString", .testJson = fmt::format( - R"({{ + R"JSON({{ "ripple_state": {{ "accounts" : ["{}","{}"], "currency": 123 }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -776,71 +776,71 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidDirectoryType", - .testJson = R"({ + .testJson = R"JSON({ "directory": 123 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, ParamTestCaseBundle{ .testName = "InvalidDirectoryIndex", - .testJson = R"({ + .testJson = R"JSON({ "directory": "123" - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidDirectoryEmptyJson", - .testJson = R"({ + .testJson = R"JSON({ "directory": {} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "missingOwnerOrDirRoot" }, ParamTestCaseBundle{ .testName = "InvalidDirectoryWrongOwnerNotString", - .testJson = R"({ + .testJson = R"JSON({ "directory": { "owner": 123 } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ownerNotString" }, ParamTestCaseBundle{ .testName = "InvalidDirectoryWrongOwnerFormat", - .testJson = R"({ + .testJson = R"JSON({ "directory": { "owner": "123" } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "InvalidDirectoryWrongDirFormat", - .testJson = R"({ + .testJson = R"JSON({ "directory": { "dir_root": "123" } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "dir_rootMalformed" }, ParamTestCaseBundle{ .testName = "InvalidDirectoryWrongDirNotString", - .testJson = R"({ + .testJson = R"JSON({ "directory": { "dir_root": 123 } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "dir_rootNotString" }, @@ -848,12 +848,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidDirectoryDirOwnerConflict", .testJson = fmt::format( - R"({{ + R"JSON({{ "directory": {{ "dir_root": "{}", "owner": "{}" }} - }})", + }})JSON", kINDEX1, kACCOUNT ), @@ -864,12 +864,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidDirectoryDirSubIndexNotInt", .testJson = fmt::format( - R"({{ + R"JSON({{ "directory": {{ "dir_root": "{}", "sub_index": "not int" }} - }})", + }})JSON", kINDEX1 ), .expectedError = "malformedRequest", @@ -878,30 +878,30 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidAMMStringIndex", - .testJson = R"({ + .testJson = R"JSON({ "amm": "invalid" - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "EmptyAMMJson", - .testJson = R"({ + .testJson = R"JSON({ "amm": {} - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "NonObjectAMMJsonAsset", - .testJson = R"({ + .testJson = R"JSON({ "amm": { "asset": 123, "asset2": 123 } - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, @@ -909,7 +909,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "EmptyAMMAssetJson", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset":{{}}, @@ -919,7 +919,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -929,7 +929,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "EmptyAMMAsset2Json", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset2":{{}}, @@ -939,7 +939,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -949,7 +949,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "MissingAMMAsset2Json", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset": @@ -958,7 +958,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -968,7 +968,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "MissingAMMAssetJson", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset2": @@ -977,7 +977,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -987,7 +987,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "AMMAssetNotJson", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset": "invalid", @@ -997,7 +997,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -1007,7 +1007,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "AMMAsset2NotJson", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset2": "invalid", @@ -1017,7 +1017,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -1027,7 +1027,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "WrongAMMAssetCurrency", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset2": @@ -1040,7 +1040,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -1050,7 +1050,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "WrongAMMAssetIssuer", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset2": @@ -1063,7 +1063,7 @@ generateTestValuesForParametersTest() "issuer" : "aa{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -1073,7 +1073,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "MissingAMMAssetIssuerForNonXRP", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset2": @@ -1086,7 +1086,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -1096,7 +1096,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "AMMAssetHasIssuerForXRP", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset2": @@ -1110,7 +1110,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT ), @@ -1121,7 +1121,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "MissingAMMAssetCurrency", .testJson = fmt::format( - R"({{ + R"JSON({{ "amm": {{ "asset2": @@ -1133,7 +1133,7 @@ generateTestValuesForParametersTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -1142,7 +1142,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeMissingBridgeAccount", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge": {{ "LockingChainDoor": "{}", @@ -1157,7 +1157,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, "JPY", @@ -1169,7 +1169,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeCurrencyIsNumber", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1185,7 +1185,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT, @@ -1198,7 +1198,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeIssuerIsNumber", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1214,7 +1214,7 @@ generateTestValuesForParametersTest() "issuer": {} }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT, @@ -1227,7 +1227,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeIssuingChainIssueIsNotObject", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1239,7 +1239,7 @@ generateTestValuesForParametersTest() }}, "IssuingChainIssue": 1 }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT @@ -1250,7 +1250,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeWithInvalidBridgeAccount", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "abcd", "bridge": {{ @@ -1266,7 +1266,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, "JPY", @@ -1278,7 +1278,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeDoorInvalid", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1294,7 +1294,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, "JPY", @@ -1306,7 +1306,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeIssuerInvalid", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1322,7 +1322,7 @@ generateTestValuesForParametersTest() "issuer": "invalid" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT, @@ -1334,7 +1334,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeIssueCurrencyInvalid", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1350,7 +1350,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT2, @@ -1362,7 +1362,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeIssueXRPCurrencyInvalid", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1379,7 +1379,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT2, @@ -1392,7 +1392,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeIssueJPYCurrencyInvalid", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1407,7 +1407,7 @@ generateTestValuesForParametersTest() "currency": "JPY" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT2 @@ -1418,7 +1418,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeMissingLockingChainDoor", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1434,7 +1434,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT2, kACCOUNT2, @@ -1446,7 +1446,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeMissingIssuingChainDoor", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1461,7 +1461,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT2 @@ -1472,7 +1472,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeMissingLockingChainIssue", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1484,7 +1484,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT, @@ -1496,7 +1496,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeMissingIssuingChainIssue", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": {{ @@ -1508,7 +1508,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT, @@ -1520,10 +1520,10 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "BridgeInvalidType", .testJson = fmt::format( - R"({{ + R"JSON({{ "bridge_account": "{}", "bridge": "invalid" - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -1531,16 +1531,16 @@ generateTestValuesForParametersTest() }, ParamTestCaseBundle{ .testName = "OwnedClaimIdInvalidType", - .testJson = R"({ + .testJson = R"JSON({ "xchain_owned_claim_id": 123 - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "OwnedClaimIdJsonMissingClaimId", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_claim_id": {{ "LockingChainDoor": "{}", @@ -1555,7 +1555,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, "JPY", @@ -1567,7 +1567,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OwnedClaimIdJsonMissingDoor", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_claim_id": {{ "xchain_owned_claim_id": 10, @@ -1582,7 +1582,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, "JPY", kACCOUNT2 @@ -1593,7 +1593,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OwnedClaimIdJsonMissingIssue", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_claim_id": {{ "xchain_owned_claim_id": 10, @@ -1604,7 +1604,7 @@ generateTestValuesForParametersTest() "currency": "XRP" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT ), @@ -1615,7 +1615,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OwnedClaimIdJsonInvalidDoor", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_claim_id": {{ "xchain_owned_claim_id": 10, @@ -1631,7 +1631,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, "JPY", kACCOUNT2 @@ -1642,7 +1642,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OwnedClaimIdJsonInvalidIssue", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_claim_id": {{ "xchain_owned_claim_id": 10, @@ -1657,7 +1657,7 @@ generateTestValuesForParametersTest() "currency": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, "JPY" @@ -1667,16 +1667,16 @@ generateTestValuesForParametersTest() }, ParamTestCaseBundle{ .testName = "OwnedCreateAccountClaimIdInvalidType", - .testJson = R"({ + .testJson = R"JSON({ "xchain_owned_create_account_claim_id": 123 - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "OwnedCreateAccountClaimIdJsonMissingClaimId", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_create_account_claim_id": {{ "LockingChainDoor": "{}", @@ -1691,7 +1691,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, "JPY", @@ -1703,7 +1703,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OwnedCreateAccountClaimIdJsonMissingDoor", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_create_account_claim_id": {{ "xchain_owned_create_account_claim_id": 10, @@ -1718,7 +1718,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, "JPY", kACCOUNT2 @@ -1729,7 +1729,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OwnedCreateAccountClaimIdJsonMissingIssue", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_create_account_claim_id": {{ "xchain_owned_create_account_claim_id": 10, @@ -1740,7 +1740,7 @@ generateTestValuesForParametersTest() "currency": "XRP" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT ), @@ -1751,7 +1751,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OwnedCreateAccountClaimIdJsonInvalidDoor", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_create_account_claim_id": {{ "xchain_owned_create_account_claim_id": 10, @@ -1767,7 +1767,7 @@ generateTestValuesForParametersTest() "issuer": "{}" }} }} - }})", + }})JSON", kACCOUNT, "JPY", kACCOUNT2 @@ -1778,7 +1778,7 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OwnedCreateAccountClaimIdJsonInvalidIssue", .testJson = fmt::format( - R"({{ + R"JSON({{ "xchain_owned_create_account_claim_id": {{ "xchain_owned_create_account_claim_id": 10, @@ -1793,7 +1793,7 @@ generateTestValuesForParametersTest() "currency": "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, "JPY" @@ -1804,11 +1804,11 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OracleObjectDocumentIdMissing", .testJson = fmt::format( - R"({{ + R"JSON({{ "oracle": {{ "account": "{}" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -1817,12 +1817,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OracleObjectDocumentIdInvalidNegative", .testJson = fmt::format( - R"({{ + R"JSON({{ "oracle": {{ "account": "{}", "oracle_document_id": -1 }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedDocumentID", @@ -1831,12 +1831,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OracleObjectDocumentIdInvalidTypeString", .testJson = fmt::format( - R"({{ + R"JSON({{ "oracle": {{ "account": "{}", "oracle_document_id": "invalid" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedDocumentID", @@ -1845,12 +1845,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OracleObjectDocumentIdInvalidTypeDouble", .testJson = fmt::format( - R"({{ + R"JSON({{ "oracle": {{ "account": "{}", "oracle_document_id": 3.21 }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedDocumentID", @@ -1859,12 +1859,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OracleObjectDocumentIdInvalidTypeObject", .testJson = fmt::format( - R"({{ + R"JSON({{ "oracle": {{ "account": "{}", "oracle_document_id": {{}} }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedDocumentID", @@ -1873,12 +1873,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OracleObjectDocumentIdInvalidTypeArray", .testJson = fmt::format( - R"({{ + R"JSON({{ "oracle": {{ "account": "{}", "oracle_document_id": [] }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedDocumentID", @@ -1887,12 +1887,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "OracleObjectDocumentIdInvalidTypeNull", .testJson = fmt::format( - R"({{ + R"JSON({{ "oracle": {{ "account": "{}", "oracle_document_id": null }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedDocumentID", @@ -1900,138 +1900,138 @@ generateTestValuesForParametersTest() }, ParamTestCaseBundle{ .testName = "OracleObjectAccountMissing", - .testJson = R"({ + .testJson = R"JSON({ "oracle": { "oracle_document_id": 1 } - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "OracleObjectAccountInvalidTypeInteger", - .testJson = R"({ + .testJson = R"JSON({ "oracle": { "account": 123, "oracle_document_id": 1 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "OracleObjectAccountInvalidTypeDouble", - .testJson = R"({ + .testJson = R"JSON({ "oracle": { "account": 123.45, "oracle_document_id": 1 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "OracleObjectAccountInvalidTypeNull", - .testJson = R"({ + .testJson = R"JSON({ "oracle": { "account": null, "oracle_document_id": 1 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "OracleObjectAccountInvalidTypeObject", - .testJson = R"({ + .testJson = R"JSON({ "oracle": { "account": {"test": "test"}, "oracle_document_id": 1 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "OracleObjectAccountInvalidTypeArray", - .testJson = R"({ + .testJson = R"JSON({ "oracle": { "account": [{"test": "test"}], "oracle_document_id": 1 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "OracleObjectAccountInvalidFormat", - .testJson = R"({ + .testJson = R"JSON({ "oracle": { "account": "NotHex", "oracle_document_id": 1 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "OracleStringInvalidFormat", - .testJson = R"({ + .testJson = R"JSON({ "oracle": "NotHex" - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "OracleStringInvalidTypeInteger", - .testJson = R"({ + .testJson = R"JSON({ "oracle": 123 - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "OracleStringInvalidTypeDouble", - .testJson = R"({ + .testJson = R"JSON({ "oracle": 123.45 - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "OracleStringInvalidTypeArray", - .testJson = R"({ + .testJson = R"JSON({ "oracle": [{"test": "test"}] - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "OracleStringInvalidTypeNull", - .testJson = R"({ + .testJson = R"JSON({ "oracle": null - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "CredentialInvalidSubjectType", - .testJson = R"({ + .testJson = R"JSON({ "credential": { "subject": 123 } - })", + })JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." }, ParamTestCaseBundle{ .testName = "CredentialInvalidIssuerType", .testJson = fmt::format( - R"({{ + R"JSON({{ "credential": {{ "issuer": ["{}"] }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -2039,34 +2039,34 @@ generateTestValuesForParametersTest() }, ParamTestCaseBundle{ .testName = "InvalidMPTIssuanceStringIndex", - .testJson = R"({ + .testJson = R"JSON({ "mpt_issuance": "invalid" - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidMPTIssuanceType", - .testJson = R"({ + .testJson = R"JSON({ "mpt_issuance": 0 - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidMPTokenStringIndex", - .testJson = R"({ + .testJson = R"JSON({ "mptoken": "invalid" - })", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidMPTokenObject", .testJson = fmt::format( - R"({{ + R"JSON({{ "mptoken": {{}} - }})" + }})JSON" ), .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." @@ -2074,11 +2074,11 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "MissingMPTokenID", .testJson = fmt::format( - R"({{ + R"JSON({{ "mptoken": {{ "account": "{}" }} - }})", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -2087,13 +2087,13 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "CredentialInvalidCredentialType", .testJson = fmt::format( - R"({{ + R"JSON({{ "credential": {{ "subject": "{}", "issuer": "{}", "credential_type": 1234 }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -2103,12 +2103,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "CredentialMissingIssuerField", .testJson = fmt::format( - R"({{ + R"JSON({{ "credential": {{ "subject": "{}", "credential_type": "1234" }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -2118,12 +2118,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidMPTokenAccount", .testJson = fmt::format( - R"({{ + R"JSON({{ "mptoken": {{ "mpt_issuance_id": "0000019315EABA24E6135A4B5CE2899E0DA791206413B33D", "account": 1 }} - }})" + }})JSON" ), .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address." @@ -2131,43 +2131,43 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidMPTokenType", .testJson = fmt::format( - R"({{ + R"JSON({{ "mptoken": 0 - }})" + }})JSON" ), .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "InvalidPermissionedDomain_NotObject", - .testJson = R"json({"permissioned_domain": []})json", + .testJson = R"JSON({"permissioned_domain": []})JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request.", }, ParamTestCaseBundle{ .testName = "InvalidPermissionedDomain_InvalidString", - .testJson = R"json({"permissioned_domain": "invalid_string"})json", + .testJson = R"JSON({"permissioned_domain": "invalid_string"})JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request.", }, ParamTestCaseBundle{ .testName = "InvalidPermissionedDomain_EmptyObject", - .testJson = R"json({"permissioned_domain": {}})json", + .testJson = R"JSON({"permissioned_domain": {}})JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request.", }, ParamTestCaseBundle{ .testName = "InvalidPermissionedDomain_BadAccount", - .testJson = R"json({"permissioned_domain": {"account": "1234", "seq": 1234}})json", + .testJson = R"JSON({"permissioned_domain": {"account": "1234", "seq": 1234}})JSON", .expectedError = "malformedAddress", .expectedErrorMessage = "Malformed address.", }, ParamTestCaseBundle{ .testName = "InvalidPermissionedDomain_MissingSeq", .testJson = fmt::format( - R"json({{ + R"JSON({{ "permissioned_domain": {{ "account": "{}" }} - }})json", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -2176,9 +2176,9 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidPermissionedDomain_SeqIsNotUint", .testJson = fmt::format( - R"json({{ + R"JSON({{ "permissioned_domain": {{ "account": "{}", "seq": -1 }} - }})json", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -2187,38 +2187,38 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "InvalidPermissionedDomain_BothAccountAndSeqAreInvalid", .testJson = - R"json({ + R"JSON({ "permissioned_domain": { "account": "", "seq": -1 } - })json", + })JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request.", }, ParamTestCaseBundle{ .testName = "Delegate_InvalidType", - .testJson = R"json({"delegate": 123})json", + .testJson = R"JSON({"delegate": 123})JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "Delegate_InvalidStringIndex", - .testJson = R"json({"delegate": "invalid_hex_string"})json", + .testJson = R"JSON({"delegate": "invalid_hex_string"})JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "Delegate_EmptyObject", - .testJson = R"json({"delegate": {}})json", + .testJson = R"JSON({"delegate": {}})JSON", .expectedError = "malformedRequest", .expectedErrorMessage = "Malformed request." }, ParamTestCaseBundle{ .testName = "Delegate_MissingAccount", .testJson = fmt::format( - R"json({{ + R"JSON({{ "delegate": {{ "authorize": "{}" }} - }})json", + }})JSON", kACCOUNT2 ), .expectedError = "malformedRequest", @@ -2227,12 +2227,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "Delegate_AccountNotString", .testJson = fmt::format( - R"json({{ + R"JSON({{ "delegate": {{ "account": 123, "authorize": "{}" }} - }})json", + }})JSON", kACCOUNT2 ), .expectedError = "malformedAddress", @@ -2241,12 +2241,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "Delegate_AccountInvalid", .testJson = fmt::format( - R"json({{ + R"JSON({{ "delegate": {{ "account": "invalid_address", "authorize": "{}" }} - }})json", + }})JSON", kACCOUNT2 ), .expectedError = "malformedAddress", @@ -2255,11 +2255,11 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "Delegate_MissingAuthorize", .testJson = fmt::format( - R"json({{ + R"JSON({{ "delegate": {{ "account": "{}" }} - }})json", + }})JSON", kACCOUNT ), .expectedError = "malformedRequest", @@ -2268,12 +2268,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "Delegate_AuthorizeNotString", .testJson = fmt::format( - R"json({{ + R"JSON({{ "delegate": {{ "account": "{}", "authorize": 123 }} - }})json", + }})JSON", kACCOUNT ), .expectedError = "malformedAddress", @@ -2282,12 +2282,12 @@ generateTestValuesForParametersTest() ParamTestCaseBundle{ .testName = "Delegate_AuthorizeInvalid", .testJson = fmt::format( - R"json({{ + R"JSON({{ "delegate": {{ "account": "{}", "authorize": "invalid_address" }} - }})json", + }})JSON", kACCOUNT ), .expectedError = "malformedAddress", @@ -2344,9 +2344,9 @@ TEST_P(IndexTest, InvalidIndexUint256) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "{}": "invalid" - }})", + }})JSON", index )); auto const output = handler.process(req, Context{yield}); @@ -2364,9 +2364,9 @@ TEST_P(IndexTest, InvalidIndexNotString) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "{}": 123 - }})", + }})JSON", index )); auto const output = handler.process(req, Context{yield}); @@ -2391,9 +2391,9 @@ TEST_F(RPCLedgerEntryTest, LedgerEntryNotFound) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "account_root": "{}" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(req, Context{yield}); @@ -2424,10 +2424,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "Index", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "index": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2436,10 +2436,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "Payment_channel", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "payment_channel": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2448,10 +2448,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "Nft_page", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "nft_page": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2462,10 +2462,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "Check", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "check": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2474,10 +2474,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DirectoryIndex", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "directory": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2486,10 +2486,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "OfferIndex", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "offer": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2500,10 +2500,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "EscrowIndex", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "escrow": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2512,10 +2512,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "TicketIndex", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "ticket": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2524,10 +2524,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DepositPreauthIndex", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "deposit_preauth": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2536,10 +2536,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "AccountRoot", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "account_root": "{}" - }})", + }})JSON", kACCOUNT ), .expectedIndex = ripple::keylet::account(getAccountIdWithString(kACCOUNT)).key, @@ -2548,10 +2548,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DID", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "did": "{}" - }})", + }})JSON", kACCOUNT ), .expectedIndex = ripple::keylet::did(getAccountIdWithString(kACCOUNT)).key, @@ -2560,13 +2560,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DirectoryViaDirRoot", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "directory": {{ "dir_root": "{}", "sub_index": 2 }} - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::keylet::page(ripple::uint256{kINDEX1}, 2).key, @@ -2575,13 +2575,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DirectoryViaOwner", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "directory": {{ "owner": "{}", "sub_index": 2 }} - }})", + }})JSON", kACCOUNT ), .expectedIndex = ripple::keylet::page(ripple::keylet::ownerDir(account1), 2).key, @@ -2590,12 +2590,12 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DirectoryViaDefaultSubIndex", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "directory": {{ "owner": "{}" }} - }})", + }})JSON", kACCOUNT ), // default sub_index is 0 @@ -2605,13 +2605,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "Escrow", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "escrow": {{ "owner": "{}", "seq": 1 }} - }})", + }})JSON", kACCOUNT ), .expectedIndex = ripple::keylet::escrow(account1, 1).key, @@ -2620,13 +2620,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DepositPreauthByAuth", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "deposit_preauth": {{ "owner": "{}", "authorized": "{}" }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -2636,7 +2636,7 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DepositPreauthByAuthCredentials", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "deposit_preauth": {{ "owner": "{}", @@ -2647,7 +2647,7 @@ generateTestValuesForNormalPathTest() }} ] }} - }})", + }})JSON", kACCOUNT, kACCOUNT2, kCREDENTIAL_TYPE @@ -2665,14 +2665,14 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "Credentials", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "credential": {{ "subject": "{}", "issuer": "{}", "credential_type": "{}" }} - }})", + }})JSON", kACCOUNT, kACCOUNT2, kCREDENTIAL_TYPE @@ -2691,13 +2691,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "RippleState", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "ripple_state": {{ "accounts": ["{}","{}"], "currency": "USD" }} - }})", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -2708,13 +2708,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "Ticket", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "ticket": {{ "account": "{}", "ticket_seq": 2 }} - }})", + }})JSON", kACCOUNT ), .expectedIndex = ripple::getTicketIndex(account1, 2), @@ -2723,13 +2723,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "Offer", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "offer": {{ "account": "{}", "seq": 2 }} - }})", + }})JSON", kACCOUNT ), .expectedIndex = ripple::keylet::offer(account1, 2).key, @@ -2740,10 +2740,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "AMMViaIndex", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "amm": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -2752,7 +2752,7 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "AMMViaJson", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "amm": {{ "asset": {{ @@ -2763,7 +2763,7 @@ generateTestValuesForNormalPathTest() "issuer": "{}" }} }} - }})", + }})JSON", "JPY", kACCOUNT2 ), @@ -2775,7 +2775,7 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "BridgeLocking", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "bridge_account": "{}", "bridge": {{ @@ -2789,7 +2789,7 @@ generateTestValuesForNormalPathTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT, kACCOUNT, kACCOUNT2, @@ -2810,7 +2810,7 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "BridgeIssuing", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "bridge_account": "{}", "bridge": {{ @@ -2824,7 +2824,7 @@ generateTestValuesForNormalPathTest() "issuer" : "{}" }} }} - }})", + }})JSON", kACCOUNT2, kACCOUNT, kACCOUNT2, @@ -2845,7 +2845,7 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "XChainOwnedClaimId", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "xchain_owned_claim_id": {{ "LockingChainDoor": "{}", @@ -2859,7 +2859,7 @@ generateTestValuesForNormalPathTest() }}, "xchain_owned_claim_id": 10 }} - }})", + }})JSON", kACCOUNT, kACCOUNT2, kACCOUNT3 @@ -2879,7 +2879,7 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "XChainOwnedCreateAccountClaimId", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "xchain_owned_create_account_claim_id": {{ "LockingChainDoor": "{}", @@ -2893,7 +2893,7 @@ generateTestValuesForNormalPathTest() }}, "xchain_owned_create_account_claim_id": 10 }} - }})", + }})JSON", kACCOUNT, kACCOUNT2, kACCOUNT3 @@ -2913,13 +2913,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "OracleEntryFoundViaIntOracleDocumentId", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "oracle": {{ "account": "{}", "oracle_document_id": 1 }} - }})", + }})JSON", kACCOUNT ), .expectedIndex = ripple::keylet::oracle(getAccountIdWithString(kACCOUNT), 1).key, @@ -2940,13 +2940,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "OracleEntryFoundViaStrOracleDocumentId", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "oracle": {{ "account": "{}", "oracle_document_id": "1" }} - }})", + }})JSON", kACCOUNT ), .expectedIndex = ripple::keylet::oracle(getAccountIdWithString(kACCOUNT), 1).key, @@ -2967,10 +2967,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "OracleEntryFoundViaString", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "oracle": "{}" - }})", + }})JSON", ripple::to_string(ripple::keylet::oracle(getAccountIdWithString(kACCOUNT), 1).key) ), .expectedIndex = ripple::keylet::oracle(getAccountIdWithString(kACCOUNT), 1).key, @@ -2991,10 +2991,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "MPTIssuance", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "mpt_issuance": "{}" - }})", + }})JSON", ripple::to_string(ripple::makeMptID(2, account1)) ), .expectedIndex = ripple::keylet::mptIssuance(ripple::makeMptID(2, account1)).key, @@ -3003,10 +3003,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "MPTokenViaIndex", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "mptoken": "{}" - }})", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -3015,13 +3015,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "MPTokenViaObject", .testJson = fmt::format( - R"({{ + R"JSON({{ "binary": true, "mptoken": {{ "account": "{}", "mpt_issuance_id": "{}" }} - }})", + }})JSON", kACCOUNT, ripple::to_string(ripple::makeMptID(2, account1)) ), @@ -3031,10 +3031,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "PermissionedDomainViaString", .testJson = fmt::format( - R"json({{ + R"JSON({{ "binary": true, "permissioned_domain": "{}" - }})json", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256(kINDEX1), @@ -3043,13 +3043,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "PermissionedDomainViaObject", .testJson = fmt::format( - R"json({{ + R"JSON({{ "binary": true, "permissioned_domain": {{ "account": "{}", "seq": {} }} - }})json", + }})JSON", kACCOUNT, kRANGE_MAX ), @@ -3061,10 +3061,10 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DelegateViaStringIndex", .testJson = fmt::format( - R"json({{ + R"JSON({{ "binary": true, "delegate": "{}" - }})json", + }})JSON", kINDEX1 ), .expectedIndex = ripple::uint256{kINDEX1}, @@ -3073,13 +3073,13 @@ generateTestValuesForNormalPathTest() NormalPathTestBundle{ .testName = "DelegateViaObject", .testJson = fmt::format( - R"json({{ + R"JSON({{ "binary": true, "delegate": {{ "account": "{}", "authorize": "{}" }} - }})json", + }})JSON", kACCOUNT, kACCOUNT2 ), @@ -3130,7 +3130,7 @@ TEST_P(RPCLedgerEntryNormalPathTest, NormalPath) // this testcase will test the deserialization of ledger entry TEST_F(RPCLedgerEntryTest, BinaryFalse) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -3149,7 +3149,7 @@ TEST_F(RPCLedgerEntryTest, BinaryFalse) "SettleDelay":300, "index":"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD" } - })"; + })JSON"; // return valid ledgerHeader auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); @@ -3163,9 +3163,9 @@ TEST_F(RPCLedgerEntryTest, BinaryFalse) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "payment_channel": "{}" - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3188,9 +3188,9 @@ TEST_F(RPCLedgerEntryTest, UnexpectedLedgerType) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "check": "{}" - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3207,10 +3207,10 @@ TEST_F(RPCLedgerEntryTest, LedgerNotExistViaIntSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "check": "{}", "ledger_index": {} - }})", + }})JSON", kINDEX1, kRANGE_MAX )); @@ -3229,10 +3229,10 @@ TEST_F(RPCLedgerEntryTest, LedgerNotExistViaStringSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "check": "{}", "ledger_index": "{}" - }})", + }})JSON", kINDEX1, kRANGE_MAX )); @@ -3251,10 +3251,10 @@ TEST_F(RPCLedgerEntryTest, LedgerNotExistViaHash) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "check": "{}", "ledger_hash": "{}" - }})", + }})JSON", kINDEX1, kLEDGER_HASH )); @@ -3270,7 +3270,7 @@ TEST_F(RPCLedgerEntryTest, InvalidEntryTypeVersion2) { runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; - auto const req = json::parse(R"({})"); + auto const req = json::parse(R"JSON({})JSON"); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -3283,7 +3283,7 @@ TEST_F(RPCLedgerEntryTest, InvalidEntryTypeVersion1) { runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; - auto const req = json::parse(R"({})"); + auto const req = json::parse(R"JSON({})JSON"); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 1}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -3310,7 +3310,7 @@ TEST(RPCLedgerEntrySpecTest, DeprecatedFields) // Expected Result: same as BinaryFalse TEST_F(RPCLedgerEntryTest, BinaryFalseIncludeDeleted) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, @@ -3329,7 +3329,7 @@ TEST_F(RPCLedgerEntryTest, BinaryFalseIncludeDeleted) "SettleDelay": 300, "index": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD" } - })"; + })JSON"; // return valid ledgerinfo auto const ledgerinfo = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); @@ -3343,10 +3343,10 @@ TEST_F(RPCLedgerEntryTest, BinaryFalseIncludeDeleted) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "index": "{}", "include_deleted": true - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3359,7 +3359,7 @@ TEST_F(RPCLedgerEntryTest, BinaryFalseIncludeDeleted) // Expected Result: return the latest object that is not deleted TEST_F(RPCLedgerEntryTest, LedgerEntryDeleted) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, @@ -3377,7 +3377,7 @@ TEST_F(RPCLedgerEntryTest, LedgerEntryDeleted) "PreviousTxnLgrSeq": 0, "index": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD" } - })"; + })JSON"; auto const ledgerinfo = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)).WillRepeatedly(Return(ledgerinfo)); // return valid ledger entry which can be deserialized @@ -3391,10 +3391,10 @@ TEST_F(RPCLedgerEntryTest, LedgerEntryDeleted) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "index": "{}", "include_deleted": true - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3419,10 +3419,10 @@ TEST_F(RPCLedgerEntryTest, LedgerEntryNotExist) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "index": "{}", "include_deleted": true - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3437,7 +3437,7 @@ TEST_F(RPCLedgerEntryTest, LedgerEntryNotExist) // Expected Result: same as BinaryFalse TEST_F(RPCLedgerEntryTest, BinaryFalseIncludeDeleteFalse) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, @@ -3456,7 +3456,7 @@ TEST_F(RPCLedgerEntryTest, BinaryFalseIncludeDeleteFalse) "SettleDelay": 300, "index": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD" } - })"; + })JSON"; // return valid ledgerinfo auto const ledgerinfo = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); @@ -3470,10 +3470,10 @@ TEST_F(RPCLedgerEntryTest, BinaryFalseIncludeDeleteFalse) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "payment_channel": "{}", "include_deleted": false - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3486,7 +3486,7 @@ TEST_F(RPCLedgerEntryTest, BinaryFalseIncludeDeleteFalse) // Expected Result: return the latest object that is not deleted (latest object in this test) TEST_F(RPCLedgerEntryTest, ObjectUpdateIncludeDelete) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, @@ -3513,7 +3513,7 @@ TEST_F(RPCLedgerEntryTest, ObjectUpdateIncludeDelete) "PreviousTxnLgrSeq": 123, "index": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD" } - })"; + })JSON"; // return valid ledgerinfo auto const ledgerinfo = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); @@ -3530,10 +3530,10 @@ TEST_F(RPCLedgerEntryTest, ObjectUpdateIncludeDelete) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "index": "{}", "include_deleted": true - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3546,7 +3546,7 @@ TEST_F(RPCLedgerEntryTest, ObjectUpdateIncludeDelete) // Expected Result: return the latest object that is not deleted TEST_F(RPCLedgerEntryTest, ObjectDeletedPreviously) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, @@ -3564,7 +3564,7 @@ TEST_F(RPCLedgerEntryTest, ObjectDeletedPreviously) "PreviousTxnLgrSeq": 0, "index": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD" } - })"; + })JSON"; auto const ledgerinfo = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)).WillRepeatedly(Return(ledgerinfo)); // return valid ledger entry which can be deserialized @@ -3578,10 +3578,10 @@ TEST_F(RPCLedgerEntryTest, ObjectDeletedPreviously) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "index": "{}", "include_deleted": true - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3604,10 +3604,10 @@ TEST_F(RPCLedgerEntryTest, ObjectSeqNotExist) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "index": "{}", "include_deleted": true - }})", + }})JSON", kINDEX1 )); auto const output = handler.process(req, Context{yield}); @@ -3621,7 +3621,7 @@ TEST_F(RPCLedgerEntryTest, ObjectSeqNotExist) // this testcase will test the if response includes synthetic mpt_issuance_id TEST_F(RPCLedgerEntryTest, SyntheticMPTIssuanceID) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -3640,7 +3640,7 @@ TEST_F(RPCLedgerEntryTest, SyntheticMPTIssuanceID) "index":"FD7E7EFAE2A20E75850D0E0590B205E2F74DC472281768CD6E03988069816336", "mpt_issuance_id":"000000024B4E9C06F24296074F7BC48F92A97916C6DC5EA9" } - })"; + })JSON"; auto const mptId = ripple::makeMptID(2, getAccountIdWithString(kACCOUNT)); @@ -3656,9 +3656,9 @@ TEST_F(RPCLedgerEntryTest, SyntheticMPTIssuanceID) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance": "{}" - }})", + }})JSON", ripple::to_string(mptId) )); auto const output = handler.process(req, Context{yield}); diff --git a/tests/unit/rpc/handlers/LedgerIndexTests.cpp b/tests/unit/rpc/handlers/LedgerIndexTests.cpp index 95381e550..1014ffd78 100644 --- a/tests/unit/rpc/handlers/LedgerIndexTests.cpp +++ b/tests/unit/rpc/handlers/LedgerIndexTests.cpp @@ -58,7 +58,7 @@ struct RPCLedgerIndexTest : HandlerBaseTestStrict { TEST_F(RPCLedgerIndexTest, DateStrNotValid) { auto const handler = AnyHandler{LedgerIndexHandler{backend_}}; - auto const req = json::parse(R"({"date": "not_a_number"})"); + auto const req = json::parse(R"JSON({"date": "not_a_number"})JSON"); runSpawn([&](auto yield) { auto const output = handler.process(req, Context{yield}); ASSERT_FALSE(output); @@ -74,7 +74,7 @@ TEST_F(RPCLedgerIndexTest, NoDateGiven) EXPECT_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)).WillOnce(Return(ledgerHeader)); auto const handler = AnyHandler{LedgerIndexHandler{backend_}}; - auto const req = json::parse(R"({})"); + auto const req = json::parse(R"JSON({})JSON"); runSpawn([&](auto yield) { auto const output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -87,7 +87,7 @@ TEST_F(RPCLedgerIndexTest, NoDateGiven) TEST_F(RPCLedgerIndexTest, EarlierThanMinLedger) { auto const handler = AnyHandler{LedgerIndexHandler{backend_}}; - auto const req = json::parse(R"({"date": "2024-06-25T12:23:05Z"})"); + auto const req = json::parse(R"JSON({"date": "2024-06-25T12:23:05Z"})JSON"); auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLEDGER_HASH, kRANGE_MIN, 1719318190); //"2024-06-25T12:23:10Z" EXPECT_CALL(*backend_, fetchLedgerBySequence(kRANGE_MIN, _)).WillOnce(Return(ledgerHeader)); @@ -104,7 +104,7 @@ TEST_F(RPCLedgerIndexTest, ChangeTimeZone) // Note: setenv/unsetenv are included with but misc-include-cleaner still angry setenv("TZ", "EST+5", 1); // NOLINT(misc-include-cleaner) auto const handler = AnyHandler{LedgerIndexHandler{backend_}}; - auto const req = json::parse(R"({"date": "2024-06-25T12:23:05Z"})"); + auto const req = json::parse(R"JSON({"date": "2024-06-25T12:23:05Z"})JSON"); auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLEDGER_HASH, kRANGE_MIN, 1719318190); //"2024-06-25T12:23:10Z" EXPECT_CALL(*backend_, fetchLedgerBySequence(kRANGE_MIN, _)).WillOnce(Return(ledgerHeader)); @@ -132,27 +132,27 @@ public: // start from 2024-06-25T12:23:10Z to 2024-06-25T12:23:50Z with step 2 return std::vector{ {.testName = "LaterThanMaxLedger", - .json = R"({"date": "2024-06-25T12:23:55Z"})", + .json = R"JSON({"date": "2024-06-25T12:23:55Z"})JSON", .expectedLedgerIndex = kRANGE_MAX, .closeTimeIso = "2024-06-25T12:23:50Z"}, {.testName = "GreaterThanMinLedger", - .json = R"({"date": "2024-06-25T12:23:11Z"})", + .json = R"JSON({"date": "2024-06-25T12:23:11Z"})JSON", .expectedLedgerIndex = kRANGE_MIN, .closeTimeIso = "2024-06-25T12:23:10Z"}, {.testName = "IsMinLedger", - .json = R"({"date": "2024-06-25T12:23:10Z"})", + .json = R"JSON({"date": "2024-06-25T12:23:10Z"})JSON", .expectedLedgerIndex = kRANGE_MIN, .closeTimeIso = "2024-06-25T12:23:10Z"}, {.testName = "IsMaxLedger", - .json = R"({"date": "2024-06-25T12:23:50Z"})", + .json = R"JSON({"date": "2024-06-25T12:23:50Z"})JSON", .expectedLedgerIndex = kRANGE_MAX, .closeTimeIso = "2024-06-25T12:23:50Z"}, {.testName = "IsMidLedger", - .json = R"({"date": "2024-06-25T12:23:30Z"})", + .json = R"JSON({"date": "2024-06-25T12:23:30Z"})JSON", .expectedLedgerIndex = 20, .closeTimeIso = "2024-06-25T12:23:30Z"}, {.testName = "BetweenLedgers", - .json = R"({"date": "2024-06-25T12:23:29Z"})", + .json = R"JSON({"date": "2024-06-25T12:23:29Z"})JSON", .expectedLedgerIndex = 19, .closeTimeIso = "2024-06-25T12:23:28Z"} }; diff --git a/tests/unit/rpc/handlers/LedgerTests.cpp b/tests/unit/rpc/handlers/LedgerTests.cpp index 9c2a0cf2f..ff554a6e6 100644 --- a/tests/unit/rpc/handlers/LedgerTests.cpp +++ b/tests/unit/rpc/handlers/LedgerTests.cpp @@ -91,85 +91,85 @@ generateTestValuesForParametersTest() return std::vector{ { .testName = "AccountsInvalidBool", - .testJson = R"({"accounts": true})", + .testJson = R"JSON({"accounts": true})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Not supported field 'accounts's value 'true'", }, { .testName = "AccountsInvalidInt", - .testJson = R"({"accounts": 123})", + .testJson = R"JSON({"accounts": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "FullInvalidBool", - .testJson = R"({"full": true})", + .testJson = R"JSON({"full": true})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Not supported field 'full's value 'true'", }, { .testName = "FullInvalidInt", - .testJson = R"({"full": 123})", + .testJson = R"JSON({"full": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "QueueExist", - .testJson = R"({"queue": true})", + .testJson = R"JSON({"queue": true})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Not supported field 'queue's value 'true'", }, { .testName = "QueueNotBool", - .testJson = R"({"queue": 123})", + .testJson = R"JSON({"queue": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "OwnerFundsNotBool", - .testJson = R"({"owner_funds": 123})", + .testJson = R"JSON({"owner_funds": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "LedgerHashInvalid", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed", }, { .testName = "LedgerHashNotString", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString", }, { .testName = "LedgerIndexNotInt", - .testJson = R"({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})", + .testJson = R"JSON({"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed", }, { .testName = "TransactionsNotBool", - .testJson = R"({"transactions": "x"})", + .testJson = R"JSON({"transactions": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "ExpandNotBool", - .testJson = R"({"expand": "x"})", + .testJson = R"JSON({"expand": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "BinaryNotBool", - .testJson = R"({"binary": "x"})", + .testJson = R"JSON({"binary": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, { .testName = "DiffNotBool", - .testJson = R"({"diff": "x"})", + .testJson = R"JSON({"diff": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters.", }, @@ -205,9 +205,9 @@ TEST_F(RPCLedgerHandlerTest, LedgerNotExistViaIntSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": {} - }})", + }})JSON", kRANGE_MAX )); auto const output = handler.process(req, Context{yield}); @@ -226,9 +226,9 @@ TEST_F(RPCLedgerHandlerTest, LedgerNotExistViaStringSequence) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": "{}" - }})", + }})JSON", kRANGE_MAX )); auto const output = handler.process(req, Context{yield}); @@ -247,9 +247,9 @@ TEST_F(RPCLedgerHandlerTest, LedgerNotExistViaHash) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_hash": "{}" - }})", + }})JSON", kLEDGER_HASH )); auto const output = handler.process(req, Context{yield}); @@ -263,7 +263,7 @@ TEST_F(RPCLedgerHandlerTest, LedgerNotExistViaHash) TEST_F(RPCLedgerHandlerTest, Default) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -281,7 +281,7 @@ TEST_F(RPCLedgerHandlerTest, Default) "total_coins":"0", "transaction_hash":"0000000000000000000000000000000000000000000000000000000000000000" } - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -307,11 +307,11 @@ TEST_F(RPCLedgerHandlerTest, ConditionallyNotSupportedFieldsDefaultValue) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "full": false, "accounts": false, "queue": false - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -326,7 +326,7 @@ TEST_F(RPCLedgerHandlerTest, QueryViaLedgerIndex) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; - auto const req = json::parse(R"({"ledger_index": 15})"); + auto const req = json::parse(R"JSON({"ledger_index": 15})JSON"); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); EXPECT_TRUE(output.result->as_object().contains("ledger")); @@ -341,7 +341,7 @@ TEST_F(RPCLedgerHandlerTest, QueryViaLedgerHash) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; - auto const req = json::parse(fmt::format(R"({{"ledger_hash": "{}" }})", kINDEX1)); + auto const req = json::parse(fmt::format(R"JSON({{"ledger_hash": "{}" }})JSON", kINDEX1)); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); EXPECT_TRUE(output.result->as_object().contains("ledger")); @@ -351,7 +351,7 @@ TEST_F(RPCLedgerHandlerTest, QueryViaLedgerHash) TEST_F(RPCLedgerHandlerTest, BinaryTrue) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -359,7 +359,7 @@ TEST_F(RPCLedgerHandlerTest, BinaryTrue) "ledger_data":"0000001E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "closed":true } - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -368,9 +368,9 @@ TEST_F(RPCLedgerHandlerTest, BinaryTrue) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": true - })" + })JSON" ); auto const output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -381,7 +381,7 @@ TEST_F(RPCLedgerHandlerTest, BinaryTrue) TEST_F(RPCLedgerHandlerTest, TransactionsExpandBinary) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -399,7 +399,7 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandBinary) } ] } - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -416,11 +416,11 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandBinary) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": true, "expand": true, "transactions": true - })" + })JSON" ); auto const output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -431,7 +431,7 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandBinary) TEST_F(RPCLedgerHandlerTest, TransactionsExpandBinaryV2) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, @@ -451,7 +451,7 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandBinaryV2) } ] } - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)).WillOnce(Return(ledgerHeader)); @@ -466,11 +466,11 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandBinaryV2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": true, "expand": true, "transactions": true - })" + })JSON" ); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2u}); ASSERT_TRUE(output); @@ -481,7 +481,7 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandBinaryV2) TEST_F(RPCLedgerHandlerTest, TransactionsExpandNotBinary) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -537,7 +537,7 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandNotBinary) } ] } - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -554,11 +554,11 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandNotBinary) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": false, "expand": true, "transactions": true - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -571,7 +571,7 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandNotBinary) TEST_F(RPCLedgerHandlerTest, TransactionsExpandNotBinaryV2) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true, @@ -637,7 +637,7 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandNotBinaryV2) } ] } - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence(kRANGE_MAX, _)).WillOnce(Return(ledgerHeader)); @@ -652,11 +652,11 @@ TEST_F(RPCLedgerHandlerTest, TransactionsExpandNotBinaryV2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": false, "expand": true, "transactions": true - })" + })JSON" ); auto output = handler.process(req, Context{.yield = yield, .apiVersion = 2u}); ASSERT_TRUE(output); @@ -685,22 +685,22 @@ TEST_F(RPCLedgerHandlerTest, TwoRequestInARowTransactionsExpandNotBinaryV2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": false, "expand": true, "transactions": true - })" + })JSON" ); auto output = handler.process(req, Context{.yield = yield, .apiVersion = 2u}); ASSERT_TRUE(output); auto const req2 = json::parse(fmt::format( - R"({{ + R"JSON({{ "binary": false, "expand": true, "transactions": true, "ledger_index": {} - }})", + }})JSON", kRANGE_MAX - 1 )); auto output2 = handler.process(req2, Context{.yield = yield, .apiVersion = 2u}); @@ -725,15 +725,15 @@ TEST_F(RPCLedgerHandlerTest, TransactionsNotExpand) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "transactions": true - })" + })JSON" ); auto const output = handler.process(req, Context{yield}); ASSERT_TRUE(output); EXPECT_EQ( output.result->as_object().at("ledger").at("transactions"), - json::parse(fmt::format(R"(["{}","{}"])", kINDEX1, kINDEX2)) + json::parse(fmt::format(R"JSON(["{}","{}"])JSON", kINDEX1, kINDEX2)) ); }); } @@ -741,7 +741,7 @@ TEST_F(RPCLedgerHandlerTest, TransactionsNotExpand) TEST_F(RPCLedgerHandlerTest, DiffNotBinary) { static constexpr auto kEXPECTED_OUT = - R"([ + R"JSON([ { "object_id":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515B1", "object":"" @@ -761,7 +761,7 @@ TEST_F(RPCLedgerHandlerTest, DiffNotBinary) "index":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" } } - ])"; + ])JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -783,9 +783,9 @@ TEST_F(RPCLedgerHandlerTest, DiffNotBinary) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "diff": true - })" + })JSON" ); auto const output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -796,7 +796,7 @@ TEST_F(RPCLedgerHandlerTest, DiffNotBinary) TEST_F(RPCLedgerHandlerTest, DiffBinary) { static constexpr auto kEXPECTED_OUT = - R"([ + R"JSON([ { "object_id":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515B1", "object":"" @@ -805,7 +805,7 @@ TEST_F(RPCLedgerHandlerTest, DiffBinary) "object_id":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC", "object":"1100612200400000240000000125000000032B000000002D00000002551B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC62400000000000000A81144B4E9C06F24296074F7BC48F92A97916C6DC5EA9" } - ])"; + ])JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -827,10 +827,10 @@ TEST_F(RPCLedgerHandlerTest, DiffBinary) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "diff": true, "binary": true - })" + })JSON" ); auto const output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -841,7 +841,7 @@ TEST_F(RPCLedgerHandlerTest, DiffBinary) TEST_F(RPCLedgerHandlerTest, OwnerFundsEmpty) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "validated":true, @@ -897,7 +897,7 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsEmpty) } ] } - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -914,12 +914,12 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsEmpty) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": false, "expand": true, "transactions": true, "owner_funds": true - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -932,7 +932,7 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsEmpty) TEST_F(RPCLedgerHandlerTest, OwnerFundsTrueBinaryFalse) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger": { "account_hash": "0000000000000000000000000000000000000000000000000000000000000000", "close_flags": 0, @@ -986,7 +986,7 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsTrueBinaryFalse) "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -1022,12 +1022,12 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsTrueBinaryFalse) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": false, "expand": true, "transactions": true, "owner_funds": true - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -1040,7 +1040,7 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsTrueBinaryFalse) TEST_F(RPCLedgerHandlerTest, OwnerFundsTrueBinaryTrue) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger": { "closed": true, "ledger_data": "0000001E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -1055,7 +1055,7 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsTrueBinaryTrue) "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -1091,12 +1091,12 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsTrueBinaryTrue) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": true, "expand": true, "transactions": true, "owner_funds": true - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -1124,12 +1124,12 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsIssuerIsSelf) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": true, "expand": true, "transactions": true, "owner_funds": true - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -1144,7 +1144,7 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsIssuerIsSelf) TEST_F(RPCLedgerHandlerTest, OwnerFundsNotEnoughForReserve) { static constexpr auto kEXPECTED_OUT = - R"({ + R"JSON({ "ledger": { "closed": true, "ledger_data": "0000001E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -1159,7 +1159,7 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsNotEnoughForReserve) "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 30, "validated": true - })"; + })JSON"; auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kRANGE_MAX); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); @@ -1193,12 +1193,12 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsNotEnoughForReserve) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": true, "expand": true, "transactions": true, "owner_funds": true - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -1239,12 +1239,12 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsNotXRP) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": true, "expand": true, "transactions": true, "owner_funds": true - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); @@ -1302,12 +1302,12 @@ TEST_F(RPCLedgerHandlerTest, OwnerFundsIgnoreFreezeLine) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{LedgerHandler{backend_, mockAmendmentCenterPtr_}}; auto const req = json::parse( - R"({ + R"JSON({ "binary": true, "expand": true, "transactions": true, "owner_funds": true - })" + })JSON" ); auto output = handler.process(req, Context{yield}); ASSERT_TRUE(output); diff --git a/tests/unit/rpc/handlers/MPTHoldersTests.cpp b/tests/unit/rpc/handlers/MPTHoldersTests.cpp index 7812e891c..2f2421c7f 100644 --- a/tests/unit/rpc/handlers/MPTHoldersTests.cpp +++ b/tests/unit/rpc/handlers/MPTHoldersTests.cpp @@ -53,20 +53,20 @@ constexpr auto kLEDGER_HASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF2 constexpr auto kMPT_ID = "000004C463C52827307480341125DA0577DEFC38405B0E3E"; std::string const kMPT_OUT1 = - R"({ + R"JSON({ "account": "rrnAZCqMahreZrKMcZU3t2DZ6yUndT4ubN", "flags": 0, "mpt_amount": "1", "mptoken_index": "D137F2E5A5767A06CB7A8F060ADE442A30CFF95028E1AF4B8767E3A56877205A" - })"; + })JSON"; std::string const kMPT_OUT2 = - R"({ + R"JSON({ "account": "rEiNkzogdHEzUxPfsri5XSMqtXUixf2Yx", "flags": 0, "mpt_amount": "1", "mptoken_index": "36D91DEE5EFE4A93119A8B84C944A528F2B444329F3846E49FE921040DE17E65" - })"; + })JSON"; } // namespace @@ -82,10 +82,10 @@ TEST_F(RPCMPTHoldersHandlerTest, NonHexLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{MPTHoldersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_hash": "xxx" - }})", + }})JSON", kMPT_ID )); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); @@ -102,10 +102,10 @@ TEST_F(RPCMPTHoldersHandlerTest, NonStringLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{MPTHoldersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_hash": 123 - }})", + }})JSON", kMPT_ID )); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); @@ -122,10 +122,10 @@ TEST_F(RPCMPTHoldersHandlerTest, InvalidLedgerIndexString) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{MPTHoldersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_index": "notvalidated" - }})", + }})JSON", kMPT_ID )); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); @@ -142,9 +142,9 @@ TEST_F(RPCMPTHoldersHandlerTest, MPTIDInvalidFormat) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{MPTHoldersHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "mpt_issuance_id": "xxx" - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -158,7 +158,7 @@ TEST_F(RPCMPTHoldersHandlerTest, MPTIDMissing) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{MPTHoldersHandler{backend_}}; - auto const input = json::parse(R"({})"); + auto const input = json::parse(R"JSON({})JSON"); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -172,9 +172,9 @@ TEST_F(RPCMPTHoldersHandlerTest, MPTIDNotString) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{MPTHoldersHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "mpt_issuance_id": 12 - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); ASSERT_FALSE(output); @@ -190,10 +190,10 @@ TEST_F(RPCMPTHoldersHandlerTest, MarkerInvalidFormat) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{MPTHoldersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "marker": "xxx" - }})", + }})JSON", kMPT_ID )); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); @@ -210,10 +210,10 @@ TEST_F(RPCMPTHoldersHandlerTest, MarkerNotString) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{MPTHoldersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "marker": 1 - }})", + }})JSON", kMPT_ID )); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); @@ -233,10 +233,10 @@ TEST_F(RPCMPTHoldersHandlerTest, NonExistLedgerViaLedgerHash) .WillByDefault(Return(std::optional{})); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kMPT_ID, kLEDGER_HASH )); @@ -257,10 +257,10 @@ TEST_F(RPCMPTHoldersHandlerTest, NonExistLedgerViaLedgerStringIndex) // mock fetchLedgerBySequence return empty EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(std::optional{})); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_index": "4" - }})", + }})JSON", kMPT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -278,10 +278,10 @@ TEST_F(RPCMPTHoldersHandlerTest, NonExistLedgerViaLedgerIntIndex) // mock fetchLedgerBySequence return empty EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(std::optional{})); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_index": 4 - }})", + }})JSON", kMPT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -303,10 +303,10 @@ TEST_F(RPCMPTHoldersHandlerTest, NonExistLedgerViaLedgerHash2) ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerinfo)); EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kMPT_ID, kLEDGER_HASH )); @@ -327,10 +327,10 @@ TEST_F(RPCMPTHoldersHandlerTest, NonExistLedgerViaLedgerIndex2) // differ from previous logic EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_index": "31" - }})", + }})JSON", kMPT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -353,10 +353,10 @@ TEST_F(RPCMPTHoldersHandlerTest, MPTNotFound) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kMPT_ID, kLEDGER_HASH )); @@ -374,13 +374,13 @@ TEST_F(RPCMPTHoldersHandlerTest, MPTNotFound) TEST_F(RPCMPTHoldersHandlerTest, DefaultParameters) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "limit":50, "ledger_index": 30, "mptokens": [{}], "validated": true - }})", + }})JSON", kMPT_ID, kMPT_OUT1 ); @@ -400,9 +400,9 @@ TEST_F(RPCMPTHoldersHandlerTest, DefaultParameters) .Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}" - }})", + }})JSON", kMPT_ID )); runSpawn([&, this](auto& yield) { @@ -418,7 +418,7 @@ TEST_F(RPCMPTHoldersHandlerTest, CustomAmounts) // it's not possible to have locked_amount to be greater than mpt_amount, // we are simply testing the response parsing of the api auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "limit":50, "ledger_index": 30, @@ -429,7 +429,7 @@ TEST_F(RPCMPTHoldersHandlerTest, CustomAmounts) "mptoken_index": "D137F2E5A5767A06CB7A8F060ADE442A30CFF95028E1AF4B8767E3A56877205A" }}], "validated": true - }})", + }})JSON", kMPT_ID ); @@ -448,9 +448,9 @@ TEST_F(RPCMPTHoldersHandlerTest, CustomAmounts) .Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}" - }})", + }})JSON", kMPT_ID )); runSpawn([&, this](auto& yield) { @@ -465,13 +465,13 @@ TEST_F(RPCMPTHoldersHandlerTest, SpecificLedgerIndex) { auto const specificLedger = 20; auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "limit":50, "ledger_index": {}, "mptokens": [{}], "validated": true - }})", + }})JSON", kMPT_ID, specificLedger, kMPT_OUT1 @@ -496,10 +496,10 @@ TEST_F(RPCMPTHoldersHandlerTest, SpecificLedgerIndex) .Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "ledger_index": {} - }})", + }})JSON", kMPT_ID, specificLedger )); @@ -514,14 +514,14 @@ TEST_F(RPCMPTHoldersHandlerTest, SpecificLedgerIndex) TEST_F(RPCMPTHoldersHandlerTest, MarkerParameter) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "limit":50, "ledger_index": 30, "mptokens": [{}], "validated": true, "marker": "{}" - }})", + }})JSON", kMPT_ID, kMPT_OUT2, ripple::strHex(getAccountIdWithString(kHOLDE_R1_ACCOUNT)) @@ -543,10 +543,10 @@ TEST_F(RPCMPTHoldersHandlerTest, MarkerParameter) auto const holder1AccountId = ripple::strHex(getAccountIdWithString(kHOLDE_R1_ACCOUNT)); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "marker": "{}" - }})", + }})JSON", kMPT_ID, holder1AccountId )); @@ -561,13 +561,13 @@ TEST_F(RPCMPTHoldersHandlerTest, MarkerParameter) TEST_F(RPCMPTHoldersHandlerTest, MultipleMPTs) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "limit":50, "ledger_index": 30, "mptokens": [{}, {}], "validated": true - }})", + }})JSON", kMPT_ID, kMPT_OUT1, kMPT_OUT2 @@ -589,9 +589,9 @@ TEST_F(RPCMPTHoldersHandlerTest, MultipleMPTs) .Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}" - }})", + }})JSON", kMPT_ID )); runSpawn([&, this](auto& yield) { @@ -605,13 +605,13 @@ TEST_F(RPCMPTHoldersHandlerTest, MultipleMPTs) TEST_F(RPCMPTHoldersHandlerTest, LimitMoreThanMAx) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "limit":100, "ledger_index": 30, "mptokens": [{}], "validated": true - }})", + }})JSON", kMPT_ID, kMPT_OUT1 ); @@ -637,10 +637,10 @@ TEST_F(RPCMPTHoldersHandlerTest, LimitMoreThanMAx) .Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "mpt_issuance_id": "{}", "limit": {} - }})", + }})JSON", kMPT_ID, MPTHoldersHandler::kLIMIT_MAX + 1 )); diff --git a/tests/unit/rpc/handlers/NFTBuyOffersTests.cpp b/tests/unit/rpc/handlers/NFTBuyOffersTests.cpp index c8ead3116..38d14db41 100644 --- a/tests/unit/rpc/handlers/NFTBuyOffersTests.cpp +++ b/tests/unit/rpc/handlers/NFTBuyOffersTests.cpp @@ -67,10 +67,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NonHexLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "xxx" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -87,10 +87,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, LimitNotInt) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": "xxx" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -106,10 +106,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, LimitNegative) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": -1 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -125,10 +125,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, LimitZero) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": 0 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -144,10 +144,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NonStringLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": 123 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -164,10 +164,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, InvalidLedgerIndexString) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "notvalidated" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -184,9 +184,9 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NFTIDInvalidFormat) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "nft_id": "00080000B4F4AFC5FBCBD76873F18006173D2193467D3EE7" - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = yield}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -200,9 +200,9 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NFTIDNotString) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "nft_id": 12 - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = yield}); ASSERT_FALSE(output); @@ -221,10 +221,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NonExistLedgerViaLedgerHash) EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -246,10 +246,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NonExistLedgerViaLedgerIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "4" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -271,10 +271,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NonExistLedgerViaLedgerHash2) ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -295,10 +295,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NonExistLedgerViaLedgerIndex2) // differ from previous logic EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "31" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -320,10 +320,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, NoNFT) ON_CALL(*backend_, doFetchLedgerObject).WillByDefault(Return(std::nullopt)); EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -342,10 +342,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, MarkerNotString) runSpawn([this](auto yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": 9 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -364,10 +364,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, InvalidMarker) runSpawn([this](auto yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": "123invalid" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -380,10 +380,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, InvalidMarker) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": 250 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -397,7 +397,7 @@ TEST_F(RPCNFTBuyOffersHandlerTest, InvalidMarker) // normal case when only provide nft_id TEST_F(RPCNFTBuyOffersHandlerTest, DefaultParameters) { - static constexpr auto kCORRECT_OUTPUT = R"({ + static constexpr auto kCORRECT_OUTPUT = R"JSON({ "nft_id": "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "validated": true, "offers": [ @@ -414,7 +414,7 @@ TEST_F(RPCNFTBuyOffersHandlerTest, DefaultParameters) "amount": "123" } ] - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -437,9 +437,9 @@ TEST_F(RPCNFTBuyOffersHandlerTest, DefaultParameters) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -477,10 +477,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, MultipleResultsWithMarkerAndLimitOutput) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": 50 - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -535,11 +535,11 @@ TEST_F(RPCNFTBuyOffersHandlerTest, ResultsForInputWithMarkerAndLimit) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC353", "limit": 50 - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -598,11 +598,11 @@ TEST_F(RPCNFTBuyOffersHandlerTest, ResultsWithoutMarkerForInputWithMarkerAndLimi runSpawn([&, this](auto yield) { auto handler = AnyHandler{NFTBuyOffersHandler{this->backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC353", "limit": 50 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -617,10 +617,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, ResultsWithoutMarkerForInputWithMarkerAndLimi runSpawn([this](auto yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": 49 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -630,10 +630,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, ResultsWithoutMarkerForInputWithMarkerAndLimi runSpawn([this](auto yield) { auto const handler = AnyHandler{NFTBuyOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": 501 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -666,10 +666,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, LimitLessThanMin) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": {} - }})", + }})JSON", kNFT_ID, NFTBuyOffersHandler::kLIMIT_MIN - 1 )); @@ -708,10 +708,10 @@ TEST_F(RPCNFTBuyOffersHandlerTest, LimitMoreThanMax) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": {} - }})", + }})JSON", kNFT_ID, NFTBuyOffersHandler::kLIMIT_MAX + 1 )); diff --git a/tests/unit/rpc/handlers/NFTHistoryTests.cpp b/tests/unit/rpc/handlers/NFTHistoryTests.cpp index 2b0d5acca..ce921a53f 100644 --- a/tests/unit/rpc/handlers/NFTHistoryTests.cpp +++ b/tests/unit/rpc/handlers/NFTHistoryTests.cpp @@ -78,164 +78,167 @@ generateTestValuesForParametersTest() return std::vector{ NFTHistoryParamTestCaseBundle{ .testName = "MissingNFTID", - .testJson = R"({})", + .testJson = R"JSON({})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'nft_id' missing" }, NFTHistoryParamTestCaseBundle{ .testName = "BinaryNotBool", - .testJson = R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "binary": 1})", + .testJson = + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "binary": 1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "ForwardNotBool", .testJson = - R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "forward": 1})", + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "forward": 1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "ledger_index_minNotInt", .testJson = - R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_min": "x"})", + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_min": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "ledger_index_maxNotInt", .testJson = - R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_max": "x"})", + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_max": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "ledger_indexInvalid", .testJson = - R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index": "x"})", + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledgerIndexMalformed" }, NFTHistoryParamTestCaseBundle{ .testName = "ledger_hashInvalid", .testJson = - R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_hash": "x"})", + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_hash": "x"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashMalformed" }, NFTHistoryParamTestCaseBundle{ .testName = "ledger_hashNotString", .testJson = - R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_hash": 123})", + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_hash": 123})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "ledger_hashNotString" }, NFTHistoryParamTestCaseBundle{ .testName = "limitNotInt", .testJson = - R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": "123"})", + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": "123"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "limitNegative", - .testJson = R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": -1})", + .testJson = + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": -1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "limitZero", - .testJson = R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": 0})", + .testJson = + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "limit": 0})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "MarkerNotObject", .testJson = - R"({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "marker": 101})", + R"JSON({"nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "marker": 101})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "invalidMarker" }, NFTHistoryParamTestCaseBundle{ .testName = "MarkerMissingSeq", - .testJson = R"({ + .testJson = R"JSON({ "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "marker": {"ledger": 123} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'seq' missing" }, NFTHistoryParamTestCaseBundle{ .testName = "MarkerMissingLedger", - .testJson = R"({ + .testJson = R"JSON({ "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "marker":{"seq": 123} - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'ledger' missing" }, NFTHistoryParamTestCaseBundle{ .testName = "MarkerLedgerNotInt", - .testJson = R"({ + .testJson = R"JSON({ "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "marker": { "seq": "string", "ledger": 1 } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "MarkerSeqNotInt", - .testJson = R"({ + .testJson = R"JSON({ "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "marker": { "ledger": "string", "seq": 1 } - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NFTHistoryParamTestCaseBundle{ .testName = "LedgerIndexMinLessThanMinSeq", - .testJson = R"({ + .testJson = R"JSON({ "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_min": 9 - })", + })JSON", .expectedError = "lgrIdxMalformed", .expectedErrorMessage = "ledgerSeqMinOutOfRange" }, NFTHistoryParamTestCaseBundle{ .testName = "LedgerIndexMaxLargeThanMaxSeq", - .testJson = R"({ + .testJson = R"JSON({ "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_max": 31 - })", + })JSON", .expectedError = "lgrIdxMalformed", .expectedErrorMessage = "ledgerSeqMaxOutOfRange" }, NFTHistoryParamTestCaseBundle{ .testName = "LedgerIndexMaxLessThanLedgerIndexMin", - .testJson = R"({ + .testJson = R"JSON({ "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_max": 11, "ledger_index_min": 20 - })", + })JSON", .expectedError = "lgrIdxsInvalid", .expectedErrorMessage = "Ledger indexes invalid." }, NFTHistoryParamTestCaseBundle{ .testName = "LedgerIndexMaxMinAndLedgerIndex", - .testJson = R"({ + .testJson = R"JSON({ "nft_id":"00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_max": 20, "ledger_index_min": 11, "ledger_index": 10 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "containsLedgerSpecifierAndRange" }, @@ -308,12 +311,12 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardTrue) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": true - }})", + }})JSON", kNFT_ID, kMIN_SEQ + 1, kMAX_SEQ - 1 @@ -323,7 +326,7 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardTrue) EXPECT_EQ(output.result->at("nft_id").as_string(), kNFT_ID); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ + 1); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ - 1); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger":12,"seq":34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_FALSE(output.result->as_object().contains("limit")); }); @@ -331,7 +334,7 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardTrue) TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardFalseV1) { - constexpr auto kOUTPUT = R"({ + constexpr auto kOUTPUT = R"JSON({ "nft_id": "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_min": 11, "ledger_index_max": 29, @@ -432,7 +435,7 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardFalseV1) "ledger": 12, "seq": 34 } - })"; + })JSON"; auto const transactions = genTransactions(kMIN_SEQ + 1, kMAX_SEQ - 1); auto const transCursor = TransactionsAndCursor{.txns = transactions, .cursor = TransactionsCursor{12, 34}}; @@ -452,12 +455,12 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardFalseV1) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kNFT_ID, kMIN_SEQ + 1, kMAX_SEQ - 1 @@ -470,7 +473,7 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardFalseV1) TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardFalseV2) { - constexpr auto kOUTPUT = R"({ + constexpr auto kOUTPUT = R"JSON({ "nft_id": "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index_min": 11, "ledger_index_max": 29, @@ -583,7 +586,7 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardFalseV2) "ledger": 12, "seq": 34 } - })"; + })JSON"; auto const transactions = genTransactions(kMIN_SEQ + 1, kMAX_SEQ - 1); auto const transCursor = TransactionsAndCursor{.txns = transactions, .cursor = TransactionsCursor{12, 34}}; @@ -606,12 +609,12 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexSpecificForwardFalseV2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kNFT_ID, kMIN_SEQ + 1, kMAX_SEQ - 1 @@ -638,12 +641,12 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexNotSpecificForwardTrue) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": true - }})", + }})JSON", kNFT_ID, -1, -1 @@ -653,7 +656,7 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexNotSpecificForwardTrue) EXPECT_EQ(output.result->at("nft_id").as_string(), kNFT_ID); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger":12,"seq":34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_FALSE(output.result->as_object().contains("limit")); }); @@ -679,12 +682,12 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexNotSpecificForwardFalse) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kNFT_ID, -1, -1 @@ -694,7 +697,7 @@ TEST_F(RPCNFTHistoryHandlerTest, IndexNotSpecificForwardFalse) EXPECT_EQ(output.result->at("nft_id").as_string(), kNFT_ID); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger":12,"seq":34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_FALSE(output.result->as_object().contains("limit")); }); @@ -720,12 +723,12 @@ TEST_F(RPCNFTHistoryHandlerTest, BinaryTrueV1) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "binary": true - }})", + }})JSON", kNFT_ID, -1, -1 @@ -735,7 +738,7 @@ TEST_F(RPCNFTHistoryHandlerTest, BinaryTrueV1) EXPECT_EQ(output.result->at("nft_id").as_string(), kNFT_ID); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger":12,"seq":34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_EQ( output.result->at("transactions").as_array()[0].as_object().at("meta").as_string(), @@ -774,12 +777,12 @@ TEST_F(RPCNFTHistoryHandlerTest, BinaryTrueV2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "binary": true - }})", + }})JSON", kNFT_ID, -1, -1 @@ -789,7 +792,7 @@ TEST_F(RPCNFTHistoryHandlerTest, BinaryTrueV2) EXPECT_EQ(output.result->at("nft_id").as_string(), kNFT_ID); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger":12,"seq":34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_EQ( output.result->at("transactions").as_array()[0].as_object().at("meta_blob").as_string(), @@ -825,14 +828,14 @@ TEST_F(RPCNFTHistoryHandlerTest, LimitAndMarker) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "limit": 2, "forward": false, "marker": {{"ledger":10,"seq":11}} - }})", + }})JSON", kNFT_ID, -1, -1 @@ -843,7 +846,7 @@ TEST_F(RPCNFTHistoryHandlerTest, LimitAndMarker) EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ); EXPECT_EQ(output.result->at("limit").as_uint64(), 2); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger":12,"seq":34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); }); } @@ -873,10 +876,10 @@ TEST_F(RPCNFTHistoryHandlerTest, SpecificLedgerIndex) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index":{} - }})", + }})JSON", kNFT_ID, kMAX_SEQ - 1 )); @@ -899,10 +902,10 @@ TEST_F(RPCNFTHistoryHandlerTest, SpecificNonexistLedgerIntIndex) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index":{} - }})", + }})JSON", kNFT_ID, kMAX_SEQ - 1 )); @@ -922,10 +925,10 @@ TEST_F(RPCNFTHistoryHandlerTest, SpecificNonexistLedgerStringIndex) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index":"{}" - }})", + }})JSON", kNFT_ID, kMAX_SEQ - 1 )); @@ -962,10 +965,10 @@ TEST_F(RPCNFTHistoryHandlerTest, SpecificLedgerHash) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_hash":"{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -1000,12 +1003,12 @@ TEST_F(RPCNFTHistoryHandlerTest, TxLessThanMinSeq) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kNFT_ID, kMIN_SEQ + 2, kMAX_SEQ - 1 @@ -1041,12 +1044,12 @@ TEST_F(RPCNFTHistoryHandlerTest, TxLargerThanMaxSeq) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false - }})", + }})JSON", kNFT_ID, kMIN_SEQ + 1, kMAX_SEQ - 2 @@ -1058,7 +1061,7 @@ TEST_F(RPCNFTHistoryHandlerTest, TxLargerThanMaxSeq) EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ - 2); EXPECT_EQ(output.result->at("transactions").as_array().size(), 1); EXPECT_FALSE(output.result->as_object().contains("limit")); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger":12,"seq":34})JSON")); }); } @@ -1082,13 +1085,13 @@ TEST_F(RPCNFTHistoryHandlerTest, LimitMoreThanMax) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTHistoryHandler{backend_}}; auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id":"{}", "ledger_index_min": {}, "ledger_index_max": {}, "forward": false, "limit": {} - }})", + }})JSON", kNFT_ID, kMIN_SEQ + 1, kMAX_SEQ - 1, @@ -1099,7 +1102,7 @@ TEST_F(RPCNFTHistoryHandlerTest, LimitMoreThanMax) EXPECT_EQ(output.result->at("nft_id").as_string(), kNFT_ID); EXPECT_EQ(output.result->at("ledger_index_min").as_uint64(), kMIN_SEQ + 1); EXPECT_EQ(output.result->at("ledger_index_max").as_uint64(), kMAX_SEQ - 1); - EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output.result->at("marker").as_object(), json::parse(R"JSON({"ledger":12,"seq":34})JSON")); EXPECT_EQ(output.result->at("transactions").as_array().size(), 2); EXPECT_EQ(output.result->as_object().at("limit").as_uint64(), NFTHistoryHandler::kLIMIT_MAX); }); diff --git a/tests/unit/rpc/handlers/NFTInfoTests.cpp b/tests/unit/rpc/handlers/NFTInfoTests.cpp index 9280b9891..da68d7f57 100644 --- a/tests/unit/rpc/handlers/NFTInfoTests.cpp +++ b/tests/unit/rpc/handlers/NFTInfoTests.cpp @@ -62,10 +62,10 @@ TEST_F(RPCNFTInfoHandlerTest, NonHexLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTInfoHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "xxx" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -82,10 +82,10 @@ TEST_F(RPCNFTInfoHandlerTest, NonStringLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTInfoHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": 123 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -102,10 +102,10 @@ TEST_F(RPCNFTInfoHandlerTest, InvalidLedgerIndexString) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTInfoHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "notvalidated" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -122,9 +122,9 @@ TEST_F(RPCNFTInfoHandlerTest, NFTIDInvalidFormat) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTInfoHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "nft_id": "00080000B4F4AFC5FBCBD76873F18006173D2193467D3EE7" - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = yield}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -138,9 +138,9 @@ TEST_F(RPCNFTInfoHandlerTest, NFTIDNotString) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTInfoHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "nft_id": 12 - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = yield}); ASSERT_FALSE(output); @@ -159,10 +159,10 @@ TEST_F(RPCNFTInfoHandlerTest, NonExistLedgerViaLedgerHash) EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -184,10 +184,10 @@ TEST_F(RPCNFTInfoHandlerTest, NonExistLedgerViaLedgerStringIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "4" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -206,10 +206,10 @@ TEST_F(RPCNFTInfoHandlerTest, NonExistLedgerViaLedgerIntIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": 4 - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -231,10 +231,10 @@ TEST_F(RPCNFTInfoHandlerTest, NonExistLedgerViaLedgerHash2) ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -255,10 +255,10 @@ TEST_F(RPCNFTInfoHandlerTest, NonExistLedgerViaLedgerIndex2) // differ from previous logic EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "31" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -281,10 +281,10 @@ TEST_F(RPCNFTInfoHandlerTest, NonExistNFT) ON_CALL(*backend_, fetchNFT).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchNFT(ripple::uint256{kNFT_ID}, 30, _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -301,7 +301,7 @@ TEST_F(RPCNFTInfoHandlerTest, NonExistNFT) // normal case when only provide nft_id TEST_F(RPCNFTInfoHandlerTest, DefaultParameters) { - static constexpr auto kCURRENT_OUTPUT = R"({ + static constexpr auto kCURRENT_OUTPUT = R"JSON({ "nft_id": "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index": 30, "owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -313,7 +313,7 @@ TEST_F(RPCNFTInfoHandlerTest, DefaultParameters) "nft_serial": 4, "uri": "757269", "validated": true - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -325,9 +325,9 @@ TEST_F(RPCNFTInfoHandlerTest, DefaultParameters) EXPECT_CALL(*backend_, fetchNFT(ripple::uint256{kNFT_ID}, 30, _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -341,7 +341,7 @@ TEST_F(RPCNFTInfoHandlerTest, DefaultParameters) // nft is burned -> should not omit uri TEST_F(RPCNFTInfoHandlerTest, BurnedNFT) { - static constexpr auto kCURRENT_OUTPUT = R"({ + static constexpr auto kCURRENT_OUTPUT = R"JSON({ "nft_id": "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index": 30, "owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -353,7 +353,7 @@ TEST_F(RPCNFTInfoHandlerTest, BurnedNFT) "nft_serial": 4, "uri": "757269", "validated": true - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -366,9 +366,9 @@ TEST_F(RPCNFTInfoHandlerTest, BurnedNFT) EXPECT_CALL(*backend_, fetchNFT(ripple::uint256{kNFT_ID}, 30, _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -382,7 +382,7 @@ TEST_F(RPCNFTInfoHandlerTest, BurnedNFT) // uri is not available -> should specify an empty string TEST_F(RPCNFTInfoHandlerTest, NotBurnedNFTWithoutURI) { - static constexpr auto kCURRENT_OUTPUT = R"({ + static constexpr auto kCURRENT_OUTPUT = R"JSON({ "nft_id": "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "ledger_index": 30, "owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -394,7 +394,7 @@ TEST_F(RPCNFTInfoHandlerTest, NotBurnedNFTWithoutURI) "nft_serial": 4, "uri": "", "validated": true - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -406,9 +406,9 @@ TEST_F(RPCNFTInfoHandlerTest, NotBurnedNFTWithoutURI) EXPECT_CALL(*backend_, fetchNFT(ripple::uint256{kNFT_ID}, 30, _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -422,7 +422,7 @@ TEST_F(RPCNFTInfoHandlerTest, NotBurnedNFTWithoutURI) // check taxon field, transfer fee and serial TEST_F(RPCNFTInfoHandlerTest, NFTWithExtraFieldsSet) { - static constexpr auto kCURRENT_OUTPUT = R"({ + static constexpr auto kCURRENT_OUTPUT = R"JSON({ "nft_id": "00081388319F12E15BCA13E1B933BF4C99C8E1BBC36BD4910A85D52F00000022", "ledger_index": 30, "owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", @@ -434,7 +434,7 @@ TEST_F(RPCNFTInfoHandlerTest, NFTWithExtraFieldsSet) "nft_serial": 34, "uri": "757269", "validated": true - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -446,9 +446,9 @@ TEST_F(RPCNFTInfoHandlerTest, NFTWithExtraFieldsSet) EXPECT_CALL(*backend_, fetchNFT(ripple::uint256{kNFT_ID2}, 30, _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}" - }})", + }})JSON", kNFT_ID2 )); runSpawn([&, this](auto yield) { diff --git a/tests/unit/rpc/handlers/NFTSellOffersTests.cpp b/tests/unit/rpc/handlers/NFTSellOffersTests.cpp index caa515ad0..23d5208be 100644 --- a/tests/unit/rpc/handlers/NFTSellOffersTests.cpp +++ b/tests/unit/rpc/handlers/NFTSellOffersTests.cpp @@ -67,10 +67,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, LimitNotInt) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": "xxx" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -86,10 +86,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, LimitNegative) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": -1 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -105,10 +105,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, LimitZero) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": 0 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -124,10 +124,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, NonHexLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "xxx" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -144,10 +144,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, NonStringLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": 123 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -164,10 +164,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, InvalidLedgerIndexString) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "notvalidated" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{.yield = yield}); @@ -184,9 +184,9 @@ TEST_F(RPCNFTSellOffersHandlerTest, NFTIDInvalidFormat) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "nft_id": "00080000B4F4AFC5FBCBD76873F18006173D2193467D3EE7" - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = yield}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -200,9 +200,9 @@ TEST_F(RPCNFTSellOffersHandlerTest, NFTIDNotString) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "nft_id": 12 - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = yield}); ASSERT_FALSE(output); @@ -221,10 +221,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, NonExistLedgerViaLedgerHash) EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -246,10 +246,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, NonExistLedgerViaLedgerIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "4" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -271,10 +271,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, NonExistLedgerViaLedgerHash2) ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -295,10 +295,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, NonExistLedgerViaLedgerIndex2) // differ from previous logic EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_index": "31" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -320,10 +320,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, NoNFT) ON_CALL(*backend_, doFetchLedgerObject).WillByDefault(Return(std::nullopt)); EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "ledger_hash": "{}" - }})", + }})JSON", kNFT_ID, kLEDGER_HASH )); @@ -342,10 +342,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, MarkerNotString) runSpawn([this](auto yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": 9 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -364,10 +364,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, InvalidMarker) runSpawn([this](auto yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": "123invalid" - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -380,10 +380,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, InvalidMarker) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": 250 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -397,7 +397,7 @@ TEST_F(RPCNFTSellOffersHandlerTest, InvalidMarker) // normal case when only provide nft_id TEST_F(RPCNFTSellOffersHandlerTest, DefaultParameters) { - static constexpr auto kCORRECT_OUTPUT = R"({ + static constexpr auto kCORRECT_OUTPUT = R"JSON({ "nft_id": "00010000A7CAD27B688D14BA1A9FA5366554D6ADCF9CE0875B974D9F00000004", "validated": true, "offers": [ @@ -414,7 +414,7 @@ TEST_F(RPCNFTSellOffersHandlerTest, DefaultParameters) "amount": "123" } ] - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(ledgerHeader)); @@ -437,9 +437,9 @@ TEST_F(RPCNFTSellOffersHandlerTest, DefaultParameters) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}" - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -477,10 +477,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, MultipleResultsWithMarkerAndLimitOutput) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": 50 - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -535,11 +535,11 @@ TEST_F(RPCNFTSellOffersHandlerTest, ResultsForInputWithMarkerAndLimit) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC353", "limit": 50 - }})", + }})JSON", kNFT_ID )); runSpawn([&, this](auto yield) { @@ -598,11 +598,11 @@ TEST_F(RPCNFTSellOffersHandlerTest, ResultsWithoutMarkerForInputWithMarkerAndLim runSpawn([&, this](auto yield) { auto handler = AnyHandler{NFTSellOffersHandler{this->backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "marker": "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC353", "limit": 50 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -617,10 +617,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, ResultsWithoutMarkerForInputWithMarkerAndLim runSpawn([this](auto yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": 49 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -630,10 +630,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, ResultsWithoutMarkerForInputWithMarkerAndLim runSpawn([this](auto yield) { auto const handler = AnyHandler{NFTSellOffersHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": 501 - }})", + }})JSON", kNFT_ID )); auto const output = handler.process(input, Context{yield}); @@ -667,10 +667,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, LimitLessThanMin) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": {} - }})", + }})JSON", kNFT_ID, NFTSellOffersHandler::kLIMIT_MIN - 1 )); @@ -710,10 +710,10 @@ TEST_F(RPCNFTSellOffersHandlerTest, LimitMoreThanMax) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "nft_id": "{}", "limit": {} - }})", + }})JSON", kNFT_ID, NFTSellOffersHandler::kLIMIT_MAX + 1 )); diff --git a/tests/unit/rpc/handlers/NFTsByIssuerTest.cpp b/tests/unit/rpc/handlers/NFTsByIssuerTest.cpp index 0000a570e..042985988 100644 --- a/tests/unit/rpc/handlers/NFTsByIssuerTest.cpp +++ b/tests/unit/rpc/handlers/NFTsByIssuerTest.cpp @@ -53,7 +53,7 @@ constexpr auto kNFT_ID2 = "00080000EC28C2910FD1C454A51598AAB91C8876286B2E7F16E5D constexpr auto kNFT_ID3 = "00080000EC28C2910FD1C454A51598AAB91C8876286B2E7F5B974D9E00000004"; // taxon 1 std::string const kNFT1_OUT = - R"({ + R"JSON({ "nft_id": "00080000EC28C2910FD1C454A51598AAB91C8876286B2E7F0000099B00000000", "ledger_index": 29, "owner": "r4X6JLsBfhNK4UnquNkCxhVHKPkvbQff67", @@ -64,9 +64,9 @@ std::string const kNFT1_OUT = "issuer": "r4X6JLsBfhNK4UnquNkCxhVHKPkvbQff67", "nft_taxon": 0, "nft_serial": 0 - })"; + })JSON"; std::string const kNFT2_OUT = - R"({ + R"JSON({ "nft_id": "00080000EC28C2910FD1C454A51598AAB91C8876286B2E7F16E5DA9C00000001", "ledger_index": 29, "owner": "r4X6JLsBfhNK4UnquNkCxhVHKPkvbQff67", @@ -77,9 +77,9 @@ std::string const kNFT2_OUT = "issuer": "r4X6JLsBfhNK4UnquNkCxhVHKPkvbQff67", "nft_taxon": 0, "nft_serial": 1 - })"; + })JSON"; std::string const kNFT3_OUT = - R"({ + R"JSON({ "nft_id": "00080000EC28C2910FD1C454A51598AAB91C8876286B2E7F5B974D9E00000004", "ledger_index": 29, "owner": "r4X6JLsBfhNK4UnquNkCxhVHKPkvbQff67", @@ -90,7 +90,7 @@ std::string const kNFT3_OUT = "issuer": "r4X6JLsBfhNK4UnquNkCxhVHKPkvbQff67", "nft_taxon": 1, "nft_serial": 4 - })"; + })JSON"; } // namespace @@ -106,10 +106,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NonHexLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTsByIssuerHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_hash": "xxx" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); @@ -126,10 +126,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NonStringLedgerHash) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTsByIssuerHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_hash": 123 - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); @@ -146,10 +146,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, InvalidLedgerIndexString) runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTsByIssuerHandler{backend_}}; auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_index": "notvalidated" - }})", + }})JSON", kACCOUNT )); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); @@ -166,9 +166,9 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NFTIssuerInvalidFormat) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTsByIssuerHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "issuer": "xxx" - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -182,7 +182,7 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NFTIssuerMissing) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTsByIssuerHandler{backend_}}; - auto const input = json::parse(R"({})"); + auto const input = json::parse(R"JSON({})JSON"); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); @@ -196,9 +196,9 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NFTIssuerNotString) { runSpawn([this](boost::asio::yield_context yield) { auto const handler = AnyHandler{NFTsByIssuerHandler{backend_}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "issuer": 12 - })"); + })JSON"); auto const output = handler.process(input, Context{.yield = std::ref(yield)}); ASSERT_FALSE(output); @@ -217,10 +217,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NonExistLedgerViaLedgerHash) .WillByDefault(Return(std::optional{})); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -241,10 +241,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NonExistLedgerViaLedgerStringIndex) // mock fetchLedgerBySequence return empty EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(std::optional{})); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_index": "4" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -262,10 +262,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NonExistLedgerViaLedgerIntIndex) // mock fetchLedgerBySequence return empty EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(std::optional{})); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_index": 4 - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -287,10 +287,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NonExistLedgerViaLedgerHash2) ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -311,10 +311,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, NonExistLedgerViaLedgerIndex2) // differ from previous logic EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(0); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_index": "31" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](boost::asio::yield_context yield) { @@ -337,10 +337,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, AccountNotFound) EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -358,13 +358,13 @@ TEST_F(RPCNFTsByIssuerHandlerTest, AccountNotFound) TEST_F(RPCNFTsByIssuerHandlerTest, DefaultParameters) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "limit": 50, "ledger_index": 30, "nfts": [{}], "validated": true - }})", + }})JSON", kACCOUNT, kNFT1_OUT ); @@ -380,9 +380,9 @@ TEST_F(RPCNFTsByIssuerHandlerTest, DefaultParameters) EXPECT_CALL(*backend_, fetchNFTsByIssuer(account, Eq(std::nullopt), Const(30), _, Eq(std::nullopt), _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto& yield) { @@ -397,7 +397,7 @@ TEST_F(RPCNFTsByIssuerHandlerTest, SpecificLedgerIndex) { auto const specificLedger = 20; auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "limit": 50, "ledger_index": {}, @@ -414,7 +414,7 @@ TEST_F(RPCNFTsByIssuerHandlerTest, SpecificLedgerIndex) "nft_serial": 0 }}], "validated": true - }})", + }})JSON", kACCOUNT, specificLedger ); @@ -433,10 +433,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, SpecificLedgerIndex) .Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "ledger_index": {} - }})", + }})JSON", kACCOUNT, specificLedger )); @@ -451,14 +451,14 @@ TEST_F(RPCNFTsByIssuerHandlerTest, SpecificLedgerIndex) TEST_F(RPCNFTsByIssuerHandlerTest, TaxonParameter) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "limit": 50, "ledger_index": 30, "nfts": [{}], "validated": true, "nft_taxon": 0 - }})", + }})JSON", kACCOUNT, kNFT1_OUT ); @@ -474,10 +474,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, TaxonParameter) EXPECT_CALL(*backend_, fetchNFTsByIssuer(account, Optional(0), Const(30), _, Eq(std::nullopt), _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "nft_taxon": 0 - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto& yield) { @@ -491,14 +491,14 @@ TEST_F(RPCNFTsByIssuerHandlerTest, TaxonParameter) TEST_F(RPCNFTsByIssuerHandlerTest, MarkerParameter) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "limit": 50, "ledger_index": 30, "nfts": [{}], "validated": true, "marker": "00080000EC28C2910FD1C454A51598AAB91C8876286B2E7F5B974D9E00000004" - }})", + }})JSON", kACCOUNT, kNFT3_OUT ); @@ -515,10 +515,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, MarkerParameter) EXPECT_CALL(*backend_, fetchNFTsByIssuer(account, _, Const(30), _, Eq(ripple::uint256{kNFT_ID1}), _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "marker": "{}" - }})", + }})JSON", kACCOUNT, kNFT_ID1 )); @@ -533,13 +533,13 @@ TEST_F(RPCNFTsByIssuerHandlerTest, MarkerParameter) TEST_F(RPCNFTsByIssuerHandlerTest, MultipleNFTs) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "limit":50, "ledger_index": 30, "nfts": [{}, {}, {}], "validated": true - }})", + }})JSON", kACCOUNT, kNFT1_OUT, kNFT2_OUT, @@ -559,9 +559,9 @@ TEST_F(RPCNFTsByIssuerHandlerTest, MultipleNFTs) EXPECT_CALL(*backend_, fetchNFTsByIssuer(account, Eq(std::nullopt), Const(30), _, Eq(std::nullopt), _)).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}" - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto& yield) { @@ -575,13 +575,13 @@ TEST_F(RPCNFTsByIssuerHandlerTest, MultipleNFTs) TEST_F(RPCNFTsByIssuerHandlerTest, LimitMoreThanMax) { auto const currentOutput = fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "limit": 100, "ledger_index": 30, "nfts": [{}], "validated": true - }})", + }})JSON", kACCOUNT, kNFT1_OUT ); @@ -601,10 +601,10 @@ TEST_F(RPCNFTsByIssuerHandlerTest, LimitMoreThanMax) .Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "issuer": "{}", "limit": {} - }})", + }})JSON", kACCOUNT, NFTsByIssuerHandler::kLIMIT_MAX + 1 )); diff --git a/tests/unit/rpc/handlers/NoRippleCheckTests.cpp b/tests/unit/rpc/handlers/NoRippleCheckTests.cpp index 95efc9833..d2eb27e6d 100644 --- a/tests/unit/rpc/handlers/NoRippleCheckTests.cpp +++ b/tests/unit/rpc/handlers/NoRippleCheckTests.cpp @@ -78,84 +78,84 @@ generateTestValuesForParametersTest() return std::vector{ NoRippleParamTestCaseBundle{ .testName = "AccountNotExists", - .testJson = R"({ + .testJson = R"JSON({ "role": "gateway" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'account' missing" }, NoRippleParamTestCaseBundle{ .testName = "AccountNotString", - .testJson = R"({ + .testJson = R"JSON({ "account": 123, "role": "gateway" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountNotString" }, NoRippleParamTestCaseBundle{ .testName = "InvalidAccount", - .testJson = R"({ + .testJson = R"JSON({ "account": "123", "role": "gateway" - })", + })JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accountMalformed" }, NoRippleParamTestCaseBundle{ .testName = "InvalidRole", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "role": "notrole" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "role field is invalid" }, NoRippleParamTestCaseBundle{ .testName = "RoleNotExists", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Required field 'role' missing" }, NoRippleParamTestCaseBundle{ .testName = "LimitNotInt", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "role": "gateway", "limit": "gg" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NoRippleParamTestCaseBundle{ .testName = "LimitNegative", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "role": "gateway", "limit": -1 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NoRippleParamTestCaseBundle{ .testName = "LimitZero", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "role": "gateway", "limit": 0 - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, NoRippleParamTestCaseBundle{ .testName = "TransactionsNotBool", - .testJson = R"({ + .testJson = R"JSON({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "role": "gateway", "transactions": "gg" - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Invalid parameters." }, @@ -186,13 +186,13 @@ TEST_P(NoRippleCheckParameterTest, InvalidParams) TEST_F(NoRippleCheckParameterTest, V1ApiTransactionsIsNotBool) { - static constexpr auto kREQ_JSON = R"( + static constexpr auto kREQ_JSON = R"JSON( { "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "role": "gateway", "transactions": "gg" } - )"; + )JSON"; EXPECT_CALL(*backend_, fetchLedgerBySequence); runSpawn([&, this](auto yield) { @@ -214,11 +214,11 @@ TEST_F(RPCNoRippleCheckTest, LedgerNotExistViaHash) ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(std::nullopt)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "role": "gateway", "ledger_hash": "{}" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -241,11 +241,11 @@ TEST_F(RPCNoRippleCheckTest, LedgerNotExistViaIntIndex) ON_CALL(*backend_, fetchLedgerBySequence(kSEQ, _)).WillByDefault(Return(std::nullopt)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "role": "gateway", "ledger_index": {} - }})", + }})JSON", kACCOUNT, kSEQ )); @@ -268,11 +268,11 @@ TEST_F(RPCNoRippleCheckTest, LedgerNotExistViaStringIndex) ON_CALL(*backend_, fetchLedgerBySequence(kSEQ, _)).WillByDefault(Return(std::nullopt)); auto static const kINPUT = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "role": "gateway", "ledger_index": "{}" - }})", + }})JSON", kACCOUNT, kSEQ )); @@ -295,11 +295,11 @@ TEST_F(RPCNoRippleCheckTest, AccountNotExist) ON_CALL(*backend_, doFetchLedgerObject).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, doFetchLedgerObject).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "gateway" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -317,7 +317,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleUserDefaultRippleSetTrustLineNoRipple { static constexpr auto kSEQ = 30; static constexpr auto kEXPECTED_OUTPUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "problems": @@ -325,7 +325,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleUserDefaultRippleSetTrustLineNoRipple "You appear to have set your default ripple flag even though you are not a gateway. This is not recommended unless you are experimenting" ], "validated":true - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ); ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); @@ -358,11 +358,11 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleUserDefaultRippleSetTrustLineNoRipple EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "user" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -378,7 +378,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleUserDefaultRippleUnsetTrustLineNoRipp { static constexpr auto kSEQ = 30; static constexpr auto kEXPECTED_OUTPUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "problems":[ @@ -386,7 +386,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleUserDefaultRippleUnsetTrustLineNoRipp "You should probably set the no ripple flag on your USD line to rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun" ], "validated":true - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ); ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); @@ -413,11 +413,11 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleUserDefaultRippleUnsetTrustLineNoRipp EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "user" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -433,7 +433,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleGatewayDefaultRippleSetTrustLineNoRip { static constexpr auto kSEQ = 30; static constexpr auto kEXPECTED_OUTPUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "problems": @@ -442,7 +442,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleGatewayDefaultRippleSetTrustLineNoRip "You should clear the no ripple flag on your USD line to rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun" ], "validated":true - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ); ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); @@ -475,11 +475,11 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleGatewayDefaultRippleSetTrustLineNoRip EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "gateway" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -495,7 +495,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleGatewayDefaultRippleUnsetTrustLineNoR { static constexpr auto kSEQ = 30; static constexpr auto kEXPECTED_OUTPUT = - R"({ + R"JSON({ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "problems": @@ -503,7 +503,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleGatewayDefaultRippleUnsetTrustLineNoR "You should immediately set your default ripple flag" ], "validated":true - })"; + })JSON"; auto ledgerHeader = createLedgerHeader(kLEDGER_HASH, kSEQ); ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); @@ -530,11 +530,11 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleGatewayDefaultRippleUnsetTrustLineNoR EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "gateway" - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -577,12 +577,12 @@ TEST_F(RPCNoRippleCheckTest, NormalPathRoleGatewayDefaultRippleUnsetTrustLineNoR EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "gateway", "transactions": true - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -630,12 +630,12 @@ TEST_F(RPCNoRippleCheckTest, NormalPathLimit) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "gateway", "limit": 1 - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -652,7 +652,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathTransactions) constexpr auto kSEQ = 30; constexpr auto kTRANSACTION_SEQ = 123; auto const expectedOutput = fmt::format( - R"({{ + R"JSON({{ "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index":30, "problems":[ @@ -694,7 +694,7 @@ TEST_F(RPCNoRippleCheckTest, NormalPathTransactions) }} ], "validated":true - }})", + }})JSON", kTRANSACTION_SEQ, kTRANSACTION_SEQ + 1, ripple::tfClearNoRipple, @@ -735,12 +735,12 @@ TEST_F(RPCNoRippleCheckTest, NormalPathTransactions) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "gateway", "transactions": true - }})", + }})JSON", kACCOUNT, kLEDGER_HASH )); @@ -787,12 +787,12 @@ TEST_F(RPCNoRippleCheckTest, LimitMoreThanMax) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "account": "{}", "ledger_hash": "{}", "role": "gateway", "limit": {} - }})", + }})JSON", kACCOUNT, kLEDGER_HASH, NoRippleCheckHandler::kLIMIT_MAX + 1 diff --git a/tests/unit/rpc/handlers/PingTests.cpp b/tests/unit/rpc/handlers/PingTests.cpp index 018d4dc1b..5e3800379 100644 --- a/tests/unit/rpc/handlers/PingTests.cpp +++ b/tests/unit/rpc/handlers/PingTests.cpp @@ -34,8 +34,8 @@ TEST_F(RPCPingHandlerTest, Default) { runSpawn([](auto yield) { auto const handler = AnyHandler{PingHandler{}}; - auto const output = handler.process(boost::json::parse(R"({})"), Context{yield}); + auto const output = handler.process(boost::json::parse(R"JSON({})JSON"), Context{yield}); ASSERT_TRUE(output); - EXPECT_EQ(output.result.value(), boost::json::parse(R"({})")); + EXPECT_EQ(output.result.value(), boost::json::parse(R"JSON({})JSON")); }); } diff --git a/tests/unit/rpc/handlers/RandomTests.cpp b/tests/unit/rpc/handlers/RandomTests.cpp index a23143bbc..082220f5e 100644 --- a/tests/unit/rpc/handlers/RandomTests.cpp +++ b/tests/unit/rpc/handlers/RandomTests.cpp @@ -35,7 +35,7 @@ TEST_F(RPCRandomHandlerTest, Default) { runSpawn([](auto yield) { auto const handler = AnyHandler{RandomHandler{}}; - auto const output = handler.process(boost::json::parse(R"({})"), Context{yield}); + auto const output = handler.process(boost::json::parse(R"JSON({})JSON"), Context{yield}); ASSERT_TRUE(output); EXPECT_TRUE(output.result->as_object().contains(JS(random))); EXPECT_EQ(output.result->as_object().at(JS(random)).as_string().size(), 64u); diff --git a/tests/unit/rpc/handlers/ServerInfoTests.cpp b/tests/unit/rpc/handlers/ServerInfoTests.cpp index 4271d230b..71564dfd4 100644 --- a/tests/unit/rpc/handlers/ServerInfoTests.cpp +++ b/tests/unit/rpc/handlers/ServerInfoTests.cpp @@ -399,11 +399,11 @@ TEST_F(RPCServerInfoHandlerTest, BackendCountersPresentWhenRequestWithParam) }}; runSpawn([&](auto yield) { - auto const req = json::parse(R"( + auto const req = json::parse(R"JSON( { "backend_counters": true } - )"); + )JSON"); auto const output = handler.process(req, Context{yield, {}, true}); validateNormalOutput(output); @@ -428,7 +428,7 @@ TEST_F(RPCServerInfoHandlerTest, RippledForwardedValuesPresent) EXPECT_CALL(*rawETLServicePtr, isAmendmentBlocked).WillOnce(Return(false)); - auto const rippledObj = json::parse(R"({ + auto const rippledObj = json::parse(R"JSON({ "result": { "info": { "build_version": "1234", @@ -437,7 +437,7 @@ TEST_F(RPCServerInfoHandlerTest, RippledForwardedValuesPresent) "network_id": 2 } } - })"); + })JSON"); EXPECT_CALL(*rawBalancerPtr, forwardToRippled).WillOnce(Return(rippledObj.as_object())); // admin calls @@ -478,11 +478,11 @@ TEST_F(RPCServerInfoHandlerTest, RippledForwardedValuesMissingNoExceptionThrown) EXPECT_CALL(*rawETLServicePtr, isAmendmentBlocked).WillOnce(Return(false)); - auto const rippledObj = json::parse(R"({ + auto const rippledObj = json::parse(R"JSON({ "result": { "info": {} } - })"); + })JSON"); EXPECT_CALL(*rawBalancerPtr, forwardToRippled).WillOnce(Return(rippledObj.as_object())); // admin calls diff --git a/tests/unit/rpc/handlers/SubscribeTests.cpp b/tests/unit/rpc/handlers/SubscribeTests.cpp index e941eb9b8..b0b0ec816 100644 --- a/tests/unit/rpc/handlers/SubscribeTests.cpp +++ b/tests/unit/rpc/handlers/SubscribeTests.cpp @@ -94,115 +94,115 @@ generateTestValuesForParametersTest() return std::vector{ SubscribeParamTestCaseBundle{ .testName = "AccountsNotArray", - .testJson = R"({"accounts": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})", + .testJson = R"JSON({"accounts": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountsNotArray" }, SubscribeParamTestCaseBundle{ .testName = "AccountsItemNotString", - .testJson = R"({"accounts": [123]})", + .testJson = R"JSON({"accounts": [123]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accounts'sItemNotString" }, SubscribeParamTestCaseBundle{ .testName = "AccountsItemInvalidString", - .testJson = R"({"accounts": ["123"]})", + .testJson = R"JSON({"accounts": ["123"]})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accounts'sItemMalformed" }, SubscribeParamTestCaseBundle{ .testName = "AccountsEmptyArray", - .testJson = R"({"accounts": []})", + .testJson = R"JSON({"accounts": []})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accounts malformed." }, SubscribeParamTestCaseBundle{ .testName = "AccountsProposedNotArray", - .testJson = R"({"accounts_proposed": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})", + .testJson = R"JSON({"accounts_proposed": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accounts_proposedNotArray" }, SubscribeParamTestCaseBundle{ .testName = "AccountsProposedItemNotString", - .testJson = R"({"accounts_proposed": [123]})", + .testJson = R"JSON({"accounts_proposed": [123]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accounts_proposed'sItemNotString" }, SubscribeParamTestCaseBundle{ .testName = "AccountsProposedItemInvalidString", - .testJson = R"({"accounts_proposed": ["123"]})", + .testJson = R"JSON({"accounts_proposed": ["123"]})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accounts_proposed'sItemMalformed" }, SubscribeParamTestCaseBundle{ .testName = "AccountsProposedEmptyArray", - .testJson = R"({"accounts_proposed": []})", + .testJson = R"JSON({"accounts_proposed": []})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accounts_proposed malformed." }, SubscribeParamTestCaseBundle{ .testName = "StreamsNotArray", - .testJson = R"({"streams": 1})", + .testJson = R"JSON({"streams": 1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "streamsNotArray" }, SubscribeParamTestCaseBundle{ .testName = "StreamNotString", - .testJson = R"({"streams": [1]})", + .testJson = R"JSON({"streams": [1]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "streamNotString" }, SubscribeParamTestCaseBundle{ .testName = "StreamNotValid", - .testJson = R"({"streams": ["1"]})", + .testJson = R"JSON({"streams": ["1"]})JSON", .expectedError = "malformedStream", .expectedErrorMessage = "Stream malformed." }, SubscribeParamTestCaseBundle{ .testName = "StreamPeerStatusNotSupport", - .testJson = R"({"streams": ["peer_status"]})", + .testJson = R"JSON({"streams": ["peer_status"]})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Operation not supported." }, SubscribeParamTestCaseBundle{ .testName = "StreamConsensusNotSupport", - .testJson = R"({"streams": ["consensus"]})", + .testJson = R"JSON({"streams": ["consensus"]})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Operation not supported." }, SubscribeParamTestCaseBundle{ .testName = "StreamServerNotSupport", - .testJson = R"({"streams": ["server"]})", + .testJson = R"JSON({"streams": ["server"]})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Operation not supported." }, SubscribeParamTestCaseBundle{ .testName = "BooksNotArray", - .testJson = R"({"books": "1"})", + .testJson = R"JSON({"books": "1"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "booksNotArray" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemNotObject", - .testJson = R"({"books": ["1"]})", + .testJson = R"JSON({"books": ["1"]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "booksItemNotObject" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemMissingTakerPays", - .testJson = R"({"books": [{"taker_gets": {"currency": "XRP"}}]})", + .testJson = R"JSON({"books": [{"taker_gets": {"currency": "XRP"}}]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Missing field 'taker_pays'" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemMissingTakerGets", - .testJson = R"({"books": [{"taker_pays": {"currency": "XRP"}}]})", + .testJson = R"JSON({"books": [{"taker_pays": {"currency": "XRP"}}]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Missing field 'taker_gets'" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsNotObject", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -212,13 +212,13 @@ generateTestValuesForParametersTest() "taker_gets": "USD" } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Field 'taker_gets' is not an object" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysNotObject", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_gets": @@ -228,13 +228,13 @@ generateTestValuesForParametersTest() "taker_pays": "USD" } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Field 'taker_pays' is not an object" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysMissingCurrency", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_gets": @@ -244,13 +244,13 @@ generateTestValuesForParametersTest() "taker_pays": {} } ] - })", + })JSON", .expectedError = "srcCurMalformed", .expectedErrorMessage = "Source currency is malformed." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsMissingCurrency", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -260,13 +260,13 @@ generateTestValuesForParametersTest() "taker_gets": {} } ] - })", + })JSON", .expectedError = "dstAmtMalformed", .expectedErrorMessage = "Destination amount/currency/issuer is malformed." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysCurrencyNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_gets": @@ -279,13 +279,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcCurMalformed", .expectedErrorMessage = "Source currency is malformed." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsCurrencyNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -298,13 +298,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstAmtMalformed", .expectedErrorMessage = "Destination amount/currency/issuer is malformed." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysInvalidCurrency", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_gets": @@ -317,13 +317,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcCurMalformed", .expectedErrorMessage = "Source currency is malformed." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsInvalidCurrency", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -336,13 +336,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstAmtMalformed", .expectedErrorMessage = "Destination amount/currency/issuer is malformed." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysMissingIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_gets": @@ -354,13 +354,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Invalid field 'taker_pays.issuer', expected non-XRP issuer." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsMissingIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -372,13 +372,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Invalid field 'taker_gets.issuer', expected non-XRP issuer." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysIssuerNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_gets": @@ -391,13 +391,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "takerPaysIssuerNotString" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsIssuerNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -410,13 +410,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "taker_gets.issuer should be string" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysInvalidIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_gets": @@ -429,13 +429,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Source issuer is malformed." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsInvalidIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -448,13 +448,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Invalid field 'taker_gets.issuer', bad issuer." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsXRPHasIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -468,13 +468,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Unneeded field 'taker_gets.issuer' for XRP currency specification." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysXRPHasIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -488,13 +488,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Unneeded field 'taker_pays.issuer' for XRP currency specification." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemBadMartket", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -506,13 +506,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "badMarket", .expectedErrorMessage = "badMarket" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemInvalidSnapshot", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -526,13 +526,13 @@ generateTestValuesForParametersTest() "snapshot": 0 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "snapshotNotBool" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemInvalidBoth", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -546,13 +546,13 @@ generateTestValuesForParametersTest() "both": 0 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "bothNotBool" }, SubscribeParamTestCaseBundle{ .testName = "BooksItemInvalidTakerNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -566,13 +566,13 @@ generateTestValuesForParametersTest() "taker": 0 } ] - })", + })JSON", .expectedError = "badIssuer", .expectedErrorMessage = "Issuer account malformed." }, SubscribeParamTestCaseBundle{ .testName = "BooksItemInvalidTaker", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { "taker_pays": @@ -586,7 +586,7 @@ generateTestValuesForParametersTest() "taker": "xxxxxxx" } ] - })", + })JSON", .expectedError = "badIssuer", .expectedErrorMessage = "Issuer account malformed." }, @@ -621,7 +621,7 @@ TEST_F(RPCSubscribeHandlerTest, EmptyResponse) auto const handler = AnyHandler{SubscribeHandler{backend_, mockAmendmentCenterPtr_, mockSubscriptionManagerPtr_}}; EXPECT_CALL(*mockSession_, setApiSubversion(0)); - auto const output = handler.process(json::parse(R"({})"), Context{yield, session_}); + auto const output = handler.process(json::parse(R"JSON({})JSON"), Context{yield, session_}); ASSERT_TRUE(output); EXPECT_TRUE(output.result->as_object().empty()); }); @@ -631,9 +631,9 @@ TEST_F(RPCSubscribeHandlerTest, StreamsWithoutLedger) { // these streams don't return response auto const input = json::parse( - R"({ + R"JSON({ "streams": ["transactions_proposed","transactions","validations","manifests","book_changes"] - })" + })JSON" ); runSpawn([&, this](auto yield) { auto const handler = @@ -654,7 +654,7 @@ TEST_F(RPCSubscribeHandlerTest, StreamsWithoutLedger) TEST_F(RPCSubscribeHandlerTest, StreamsLedger) { static constexpr auto kEXPECTED_OUTPUT = - R"({ + R"JSON({ "validated_ledgers":"10-30", "ledger_index":30, "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", @@ -662,12 +662,12 @@ TEST_F(RPCSubscribeHandlerTest, StreamsLedger) "fee_base":1, "reserve_base":3, "reserve_inc":2 - })"; + })JSON"; auto const input = json::parse( - R"({ + R"JSON({ "streams": ["ledger"] - })" + })JSON" ); runSpawn([&, this](auto yield) { auto const handler = @@ -686,9 +686,9 @@ TEST_F(RPCSubscribeHandlerTest, StreamsLedger) TEST_F(RPCSubscribeHandlerTest, Accounts) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "accounts": ["{}","{}","{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kACCOUNT2 @@ -709,9 +709,9 @@ TEST_F(RPCSubscribeHandlerTest, Accounts) TEST_F(RPCSubscribeHandlerTest, AccountsProposed) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "accounts_proposed": ["{}","{}","{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2, kACCOUNT2 @@ -733,7 +733,7 @@ TEST_F(RPCSubscribeHandlerTest, AccountsProposed) TEST_F(RPCSubscribeHandlerTest, JustBooks) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "books": [ {{ @@ -748,7 +748,7 @@ TEST_F(RPCSubscribeHandlerTest, JustBooks) }} }} ] - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -765,7 +765,7 @@ TEST_F(RPCSubscribeHandlerTest, JustBooks) TEST_F(RPCSubscribeHandlerTest, BooksBothSet) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "books": [ {{ @@ -781,7 +781,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSet) "both": true }} ] - }})", + }})JSON", kACCOUNT )); runSpawn([&, this](auto yield) { @@ -798,7 +798,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSet) TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "books": [ {{ @@ -815,7 +815,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet) "snapshot": true }} ] - }})", + }})JSON", kACCOUNT )); backend_->setRange(kMIN_SEQ, kMAX_SEQ); @@ -899,7 +899,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(2); static auto const kEXPECTED_OFFER = fmt::format( - R"({{ + R"JSON({{ "Account":"{}", "BookDirectory":"{}", "BookNode":"0", @@ -919,13 +919,13 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet) "index":"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321", "owner_funds":"193", "quality":"2" - }})", + }})JSON", kACCOUNT2, kPAYS20_USD_GETS10_XRP_BOOK_DIR, kACCOUNT ); static auto const kEXPECTED_REVERSED_OFFER = fmt::format( - R"({{ + R"JSON({{ "Account":"{}", "BookDirectory":"{}", "BookNode":"0", @@ -945,7 +945,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet) "index":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC", "owner_funds":"10", "quality":"2" - }})", + }})JSON", kACCOUNT, kPAYS20_XRP_GETS10_USD_BOOK_DIR, kACCOUNT @@ -969,7 +969,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet) TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "books": [ {{ @@ -985,7 +985,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet) "snapshot": true }} ] - }})", + }})JSON", kACCOUNT )); backend_->setRange(kMIN_SEQ, kMAX_SEQ); @@ -1068,7 +1068,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet) EXPECT_CALL(*backend_, doFetchLedgerObjects); static auto const kEXPECTED_OFFER = fmt::format( - R"({{ + R"JSON({{ "Account":"{}", "BookDirectory":"{}", "BookNode":"0", @@ -1088,7 +1088,7 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet) "index":"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321", "owner_funds":"193", "quality":"2" - }})", + }})JSON", kACCOUNT2, kPAYS20_USD_GETS10_XRP_BOOK_DIR, kACCOUNT @@ -1109,9 +1109,9 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet) TEST_F(RPCSubscribeHandlerTest, APIVersion) { auto const input = json::parse( - R"({ + R"JSON({ "streams": ["transactions_proposed"] - })" + })JSON" ); auto const apiVersion = 2; runSpawn([&, this](auto yield) { diff --git a/tests/unit/rpc/handlers/TestHandlerTests.cpp b/tests/unit/rpc/handlers/TestHandlerTests.cpp index 7996e950f..faf929791 100644 --- a/tests/unit/rpc/handlers/TestHandlerTests.cpp +++ b/tests/unit/rpc/handlers/TestHandlerTests.cpp @@ -40,10 +40,10 @@ TEST_F(RPCTestHandlerTest, HandlerSuccess) { runSpawn([](auto yield) { auto const handler = AnyHandler{HandlerFake{}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "hello": "world", "limit": 10 - })"); + })JSON"); auto const output = handler.process(input, Context{yield}); ASSERT_TRUE(output); @@ -57,7 +57,7 @@ TEST_F(RPCTestHandlerTest, NoInputHandlerSuccess) { runSpawn([](auto yield) { auto const handler = AnyHandler{NoInputHandlerFake{}}; - auto const output = handler.process(json::parse(R"({})"), Context{yield}); + auto const output = handler.process(json::parse(R"JSON({})JSON"), Context{yield}); ASSERT_TRUE(output); auto const val = output.result.value(); @@ -69,10 +69,10 @@ TEST_F(RPCTestHandlerTest, HandlerErrorHandling) { runSpawn([](auto yield) { auto const handler = AnyHandler{HandlerFake{}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "hello": "not world", "limit": 10 - })"); + })JSON"); auto const output = handler.process(input, Context{yield}); ASSERT_FALSE(output); @@ -88,10 +88,10 @@ TEST_F(RPCTestHandlerTest, HandlerInnerErrorHandling) { runSpawn([](auto yield) { auto const handler = AnyHandler{FailingHandlerFake{}}; - auto const input = json::parse(R"({ + auto const input = json::parse(R"JSON({ "hello": "world", "limit": 10 - })"); + })JSON"); // validation succeeds but handler itself returns error auto const output = handler.process(input, Context{yield}); diff --git a/tests/unit/rpc/handlers/TransactionEntryTests.cpp b/tests/unit/rpc/handlers/TransactionEntryTests.cpp index a3e4c7d63..13403a79f 100644 --- a/tests/unit/rpc/handlers/TransactionEntryTests.cpp +++ b/tests/unit/rpc/handlers/TransactionEntryTests.cpp @@ -72,7 +72,7 @@ TEST_F(RPCTransactionEntryHandlerTest, TxHashWrongFormat) { runSpawn([this](auto yield) { auto const handler = AnyHandler{TransactionEntryHandler{backend_}}; - auto const output = handler.process(json::parse(R"({"tx_hash":"123"})"), Context{yield}); + auto const output = handler.process(json::parse(R"JSON({"tx_hash":"123"})JSON"), Context{yield}); ASSERT_FALSE(output); auto const err = rpc::makeError(output.result.error()); EXPECT_EQ(err.at("error").as_string(), "invalidParams"); @@ -88,10 +88,10 @@ TEST_F(RPCTransactionEntryHandlerTest, NonExistLedgerViaLedgerHash) EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_hash": "{}", "tx_hash": "{}" - }})", + }})JSON", kINDEX, kTXN_ID )); @@ -112,10 +112,10 @@ TEST_F(RPCTransactionEntryHandlerTest, NonExistLedgerViaLedgerIndex) ON_CALL(*backend_, fetchLedgerBySequence).WillByDefault(Return(std::optional{})); EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "ledger_index": "4", "tx_hash": "{}" - }})", + }})JSON", kTXN_ID )); runSpawn([&, this](auto yield) { @@ -138,9 +138,9 @@ TEST_F(RPCTransactionEntryHandlerTest, TXNotFound) runSpawn([this](auto yield) { auto const handler = AnyHandler{TransactionEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "tx_hash": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -168,10 +168,10 @@ TEST_F(RPCTransactionEntryHandlerTest, LedgerSeqNotMatch) runSpawn([this](auto yield) { auto const handler = AnyHandler{TransactionEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "tx_hash": "{}", "ledger_index": "30" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -184,7 +184,7 @@ TEST_F(RPCTransactionEntryHandlerTest, LedgerSeqNotMatch) TEST_F(RPCTransactionEntryHandlerTest, NormalPath) { - static constexpr auto kOUTPUT = R"({ + static constexpr auto kOUTPUT = R"JSON({ "metadata": { "AffectedNodes": @@ -228,7 +228,7 @@ TEST_F(RPCTransactionEntryHandlerTest, NormalPath) "ledger_index": 30, "ledger_hash": "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC322", "validated": true - })"; + })JSON"; TransactionAndMetadata tx; tx.metadata = createMetaDataForCreateOffer(kCURRENCY, kACCOUNT, 100, 200, 300).getSerializer().peekData(); @@ -245,10 +245,10 @@ TEST_F(RPCTransactionEntryHandlerTest, NormalPath) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{TransactionEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "tx_hash": "{}", "ledger_index": {} - }})", + }})JSON", kTXN_ID, tx.ledgerSequence )); @@ -260,7 +260,7 @@ TEST_F(RPCTransactionEntryHandlerTest, NormalPath) TEST_F(RPCTransactionEntryHandlerTest, NormalPathV2) { - static constexpr auto kOUTPUT = R"({ + static constexpr auto kOUTPUT = R"JSON({ "meta": { "AffectedNodes": @@ -305,7 +305,7 @@ TEST_F(RPCTransactionEntryHandlerTest, NormalPathV2) "close_time_iso": "2000-01-01T00:00:00Z", "hash": "2E2FBAAFF767227FE4381C4BE9855986A6B9F96C62F6E443731AB36F7BBB8A08", "validated": true - })"; + })JSON"; TransactionAndMetadata tx; tx.metadata = createMetaDataForCreateOffer(kCURRENCY, kACCOUNT, 100, 200, 300).getSerializer().peekData(); @@ -319,10 +319,10 @@ TEST_F(RPCTransactionEntryHandlerTest, NormalPathV2) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{TransactionEntryHandler{backend_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "tx_hash": "{}", "ledger_index": {} - }})", + }})JSON", kTXN_ID, tx.ledgerSequence )); diff --git a/tests/unit/rpc/handlers/TxTests.cpp b/tests/unit/rpc/handlers/TxTests.cpp index 57bea26ed..60c90fb9c 100644 --- a/tests/unit/rpc/handlers/TxTests.cpp +++ b/tests/unit/rpc/handlers/TxTests.cpp @@ -58,7 +58,7 @@ constexpr auto kCURRENCY = "0158415500000000C1F76FF6ECB0BAC600000000"; constexpr auto kCTID = "C002807000010002"; // seq 163952 txindex 1 netid 2 constexpr auto kSEQ_FROM_CTID = 163952; -constexpr auto kDEFAULT_OUT1 = R"({ +constexpr auto kDEFAULT_OUT1 = R"JSON({ "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Fee": "2", "Sequence": 100, @@ -95,9 +95,9 @@ constexpr auto kDEFAULT_OUT1 = R"({ "ledger_index": 100, "inLedger": 100, "validated": true -})"; +})JSON"; -constexpr auto kDEFAULT_OUT2 = R"({ +constexpr auto kDEFAULT_OUT2 = R"JSON({ "hash": "2E2FBAAFF767227FE4381C4BE9855986A6B9F96C62F6E443731AB36F7BBB8A08", "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_index": 100, @@ -139,7 +139,7 @@ constexpr auto kDEFAULT_OUT2 = R"({ }, "close_time_iso": "2000-01-01T00:00:00Z", "validated": true -})"; +})JSON"; constexpr auto kINDEX = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC322"; } // namespace @@ -151,12 +151,12 @@ TEST_F(RPCTxTest, ExcessiveLgrRange) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "min_ledger": 1, "max_ledger": 1002 - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -185,11 +185,11 @@ TEST_F(RPCTxTest, InvalidBinaryV1) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "binary": 12 - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 1u}); @@ -202,11 +202,11 @@ TEST_F(RPCTxTest, InvalidBinaryV2) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "binary": 12 - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2u}); @@ -223,12 +223,12 @@ TEST_F(RPCTxTest, InvalidLgrRange) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "max_ledger": 1, "min_ledger": 10 - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -252,10 +252,10 @@ TEST_F(RPCTxTest, TxnNotFound) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -280,12 +280,12 @@ TEST_F(RPCTxTest, TxnNotFoundInGivenRangeSearchAllFalse) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "min_ledger": 1, "max_ledger": 1000 - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -311,12 +311,12 @@ TEST_F(RPCTxTest, TxnNotFoundInGivenRangeSearchAllTrue) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "min_ledger": 1, "max_ledger": 1000 - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -343,11 +343,11 @@ TEST_F(RPCTxTest, CtidNotFoundSearchAllFalse) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "ctid": "{}", "min_ledger": 1, "max_ledger": 1000 - }})", + }})JSON", kCTID )); auto const output = handler.process(req, Context{yield}); @@ -378,10 +378,10 @@ TEST_F(RPCTxTest, DefaultParameter_API_v1) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 1u}); @@ -408,10 +408,10 @@ TEST_F(RPCTxTest, PaymentTx_API_v1) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 1u}); @@ -439,10 +439,10 @@ TEST_F(RPCTxTest, PaymentTx_API_v2) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2u}); @@ -473,10 +473,10 @@ TEST_F(RPCTxTest, DefaultParameter_API_v2) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2u}); @@ -488,7 +488,7 @@ TEST_F(RPCTxTest, DefaultParameter_API_v2) TEST_F(RPCTxTest, ReturnBinary) { // Note: `inLedger` is API v1 only. See DefaultOutput_* - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "meta": "201C00000064F8E311006FE864D50AA87BEE5380000158415500000000C1F76FF6ECB0BAC6000000004B4E9C06F24296074F7BC48F92A97916C6DC5EA96540000000000000C8E1E1F1031000", "tx": "120007240000006464400000000000012C65D5071AFD498D00000158415500000000C1F76FF6ECB0BAC600000000D31252CF902EF8DD8451243869B38667CBD89DF368400000000000000273047465737481144B4E9C06F24296074F7BC48F92A97916C6DC5EA9", "hash": "2E2FBAAFF767227FE4381C4BE9855986A6B9F96C62F6E443731AB36F7BBB8A08", @@ -497,7 +497,7 @@ TEST_F(RPCTxTest, ReturnBinary) "inLedger": 100, "validated": true, "ctid": "C000006400640000" - })"; + })JSON"; TransactionAndMetadata tx; tx.metadata = createMetaDataForCreateOffer(kCURRENCY, kACCOUNT, 100, 200, 300).getSerializer().peekData(); @@ -514,11 +514,11 @@ TEST_F(RPCTxTest, ReturnBinary) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "binary": true - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -531,7 +531,7 @@ TEST_F(RPCTxTest, ReturnBinary) TEST_F(RPCTxTest, ReturnBinaryWithCTID) { // Note: `inLedger` is API v1 only. See DefaultOutput_* - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "meta": "201C00000064F8E311006FE864D50AA87BEE5380000158415500000000C1F76FF6ECB0BAC6000000004B4E9C06F24296074F7BC48F92A97916C6DC5EA96540000000000000C8E1E1F1031000", "tx": "120007240000006464400000000000012C65D5071AFD498D00000158415500000000C1F76FF6ECB0BAC600000000D31252CF902EF8DD8451243869B38667CBD89DF368400000000000000273047465737481144B4E9C06F24296074F7BC48F92A97916C6DC5EA9", "hash": "2E2FBAAFF767227FE4381C4BE9855986A6B9F96C62F6E443731AB36F7BBB8A08", @@ -540,7 +540,7 @@ TEST_F(RPCTxTest, ReturnBinaryWithCTID) "inLedger": 100, "ctid": "C000006400640002", "validated": true - })"; + })JSON"; TransactionAndMetadata tx; tx.metadata = createMetaDataForCreateOffer(kCURRENCY, kACCOUNT, 100, 200, 300).getSerializer().peekData(); @@ -557,11 +557,11 @@ TEST_F(RPCTxTest, ReturnBinaryWithCTID) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "binary": true - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -574,7 +574,7 @@ TEST_F(RPCTxTest, MintNFT) { // Note: `inLedger` is API v1 only. See DefaultOutput_* auto static const kOUT = fmt::format( - R"({{ + R"JSON({{ "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Fee": "50", "NFTokenTaxon": 123, @@ -628,7 +628,7 @@ TEST_F(RPCTxTest, MintNFT) "ledger_index": 100, "inLedger": 100, "validated": true - }})", + }})JSON", kNFT_ID, kNFT_ID ); @@ -645,10 +645,10 @@ TEST_F(RPCTxTest, MintNFT) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -672,10 +672,10 @@ TEST_F(RPCTxTest, NFTAcceptOffer) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -700,10 +700,10 @@ TEST_F(RPCTxTest, NFTCancelOffer) runSpawn([this, &ids](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -735,10 +735,10 @@ TEST_F(RPCTxTest, NFTCreateOffer) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -752,11 +752,11 @@ TEST_F(RPCTxTest, CTIDAndTransactionBothProvided) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}", "ctid": "{}" - }})", + }})JSON", kTXN_ID, kCTID )); @@ -773,7 +773,7 @@ TEST_F(RPCTxTest, CTIDAndTransactionBothNotProvided) { runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; - auto const req = json::parse(R"({ "command": "tx"})"); + auto const req = json::parse(R"JSON({ "command": "tx"})JSON"); auto const output = handler.process(req, Context{yield}); ASSERT_FALSE(output); @@ -787,7 +787,7 @@ TEST_F(RPCTxTest, CTIDInvalidType) { runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; - auto const req = json::parse(R"({ "command": "tx", "ctid": 123})"); + auto const req = json::parse(R"JSON({ "command": "tx", "ctid": 123})JSON"); auto const output = handler.process(req, Context{yield}); ASSERT_FALSE(output); @@ -805,7 +805,7 @@ TEST_F(RPCTxTest, CTIDInvalidString) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; - auto const req = json::parse(R"({ "command": "tx", "ctid": "B002807000010002"})"); + auto const req = json::parse(R"JSON({ "command": "tx", "ctid": "B002807000010002"})JSON"); auto const output = handler.process(req, Context{yield}); ASSERT_FALSE(output); @@ -824,10 +824,10 @@ TEST_F(RPCTxTest, CTIDNotMatch) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "ctid": "{}" - }})", + }})JSON", kCTID )); auto const output = handler.process(req, Context{yield}); @@ -845,7 +845,7 @@ TEST_F(RPCTxTest, CTIDNotMatch) TEST_F(RPCTxTest, ReturnCTIDForTxInput) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Fee":"2", "Sequence":100, @@ -888,7 +888,7 @@ TEST_F(RPCTxTest, ReturnCTIDForTxInput) "ledger_index":100, "inLedger":100, "validated": true - })"; + })JSON"; TransactionAndMetadata tx; tx.metadata = createMetaDataForCreateOffer(kCURRENCY, kACCOUNT, 100, 200, 300).getSerializer().peekData(); @@ -905,10 +905,10 @@ TEST_F(RPCTxTest, ReturnCTIDForTxInput) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -919,7 +919,7 @@ TEST_F(RPCTxTest, ReturnCTIDForTxInput) TEST_F(RPCTxTest, NotReturnCTIDIfETLNotAvailable) { - static constexpr auto kOUT = R"({ + static constexpr auto kOUT = R"JSON({ "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Fee":"2", "Sequence":100, @@ -961,7 +961,7 @@ TEST_F(RPCTxTest, NotReturnCTIDIfETLNotAvailable) "ledger_index":100, "inLedger":100, "validated": true - })"; + })JSON"; TransactionAndMetadata tx; tx.metadata = createMetaDataForCreateOffer(kCURRENCY, kACCOUNT, 100, 200, 300).getSerializer().peekData(); @@ -978,10 +978,10 @@ TEST_F(RPCTxTest, NotReturnCTIDIfETLNotAvailable) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "transaction": "{}" - }})", + }})JSON", kTXN_ID )); auto const output = handler.process(req, Context{yield}); @@ -993,7 +993,7 @@ TEST_F(RPCTxTest, NotReturnCTIDIfETLNotAvailable) TEST_F(RPCTxTest, ViaCTID) { auto static const kOUT = fmt::format( - R"({{ + R"JSON({{ "Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Fee":"2", "Sequence":100, @@ -1036,7 +1036,7 @@ TEST_F(RPCTxTest, ViaCTID) "ledger_index":{}, "inLedger":{}, "validated": true - }})", + }})JSON", kCTID, kSEQ_FROM_CTID, kSEQ_FROM_CTID @@ -1063,10 +1063,10 @@ TEST_F(RPCTxTest, ViaCTID) runSpawn([this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "ctid": "{}" - }})", + }})JSON", kCTID )); auto const output = handler.process(req, Context{yield}); @@ -1101,10 +1101,10 @@ TEST_F(RPCTxTest, ViaLowercaseCTID) runSpawn([&, this](auto yield) { auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; auto const req = json::parse(fmt::format( - R"({{ + R"JSON({{ "command": "tx", "ctid": "{}" - }})", + }})JSON", ctid )); auto const output = handler.process(req, Context{yield}); diff --git a/tests/unit/rpc/handlers/UnsubscribeTests.cpp b/tests/unit/rpc/handlers/UnsubscribeTests.cpp index d469aef76..f46f4021f 100644 --- a/tests/unit/rpc/handlers/UnsubscribeTests.cpp +++ b/tests/unit/rpc/handlers/UnsubscribeTests.cpp @@ -76,97 +76,97 @@ generateTestValuesForParametersTest() return std::vector{ UnsubscribeParamTestCaseBundle{ .testName = "AccountsNotArray", - .testJson = R"({"accounts": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})", + .testJson = R"JSON({"accounts": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accountsNotArray" }, UnsubscribeParamTestCaseBundle{ .testName = "AccountsItemNotString", - .testJson = R"({"accounts": [123]})", + .testJson = R"JSON({"accounts": [123]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accounts'sItemNotString" }, UnsubscribeParamTestCaseBundle{ .testName = "AccountsItemInvalidString", - .testJson = R"({"accounts": ["123"]})", + .testJson = R"JSON({"accounts": ["123"]})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accounts'sItemMalformed" }, UnsubscribeParamTestCaseBundle{ .testName = "AccountsEmptyArray", - .testJson = R"({"accounts": []})", + .testJson = R"JSON({"accounts": []})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accounts malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "AccountsProposedNotArray", - .testJson = R"({"accounts_proposed": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})", + .testJson = R"JSON({"accounts_proposed": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accounts_proposedNotArray" }, UnsubscribeParamTestCaseBundle{ .testName = "AccountsProposedItemNotString", - .testJson = R"({"accounts_proposed": [123]})", + .testJson = R"JSON({"accounts_proposed": [123]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "accounts_proposed'sItemNotString" }, UnsubscribeParamTestCaseBundle{ .testName = "AccountsProposedItemInvalidString", - .testJson = R"({"accounts_proposed": ["123"]})", + .testJson = R"JSON({"accounts_proposed": ["123"]})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accounts_proposed'sItemMalformed" }, UnsubscribeParamTestCaseBundle{ .testName = "AccountsProposedEmptyArray", - .testJson = R"({"accounts_proposed": []})", + .testJson = R"JSON({"accounts_proposed": []})JSON", .expectedError = "actMalformed", .expectedErrorMessage = "accounts_proposed malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "StreamsNotArray", - .testJson = R"({"streams": 1})", + .testJson = R"JSON({"streams": 1})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "streamsNotArray" }, UnsubscribeParamTestCaseBundle{ .testName = "StreamNotString", - .testJson = R"({"streams": [1]})", + .testJson = R"JSON({"streams": [1]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "streamNotString" }, UnsubscribeParamTestCaseBundle{ .testName = "StreamNotValid", - .testJson = R"({"streams": ["1"]})", + .testJson = R"JSON({"streams": ["1"]})JSON", .expectedError = "malformedStream", .expectedErrorMessage = "Stream malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksNotArray", - .testJson = R"({"books": "1"})", + .testJson = R"JSON({"books": "1"})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "booksNotArray" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemNotObject", - .testJson = R"({"books": ["1"]})", + .testJson = R"JSON({"books": ["1"]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "booksItemNotObject" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemMissingTakerPays", - .testJson = R"({"books": [{"taker_gets": {"currency": "XRP"}}]})", + .testJson = R"JSON({"books": [{"taker_gets": {"currency": "XRP"}}]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Missing field 'taker_pays'" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemMissingTakerGets", - .testJson = R"({"books": [{"taker_pays": {"currency": "XRP"}}]})", + .testJson = R"JSON({"books": [{"taker_pays": {"currency": "XRP"}}]})JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Missing field 'taker_gets'" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsNotObject", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -177,13 +177,13 @@ generateTestValuesForParametersTest() "taker_gets": "USD" } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Field 'taker_gets' is not an object" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysNotObject", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -194,13 +194,13 @@ generateTestValuesForParametersTest() "taker_pays": "USD" } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "Field 'taker_pays' is not an object" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysMissingCurrency", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -211,13 +211,13 @@ generateTestValuesForParametersTest() "taker_pays": {} } ] - })", + })JSON", .expectedError = "srcCurMalformed", .expectedErrorMessage = "Source currency is malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsMissingCurrency", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -228,13 +228,13 @@ generateTestValuesForParametersTest() "taker_gets": {} } ] - })", + })JSON", .expectedError = "dstAmtMalformed", .expectedErrorMessage = "Destination amount/currency/issuer is malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysCurrencyNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -248,13 +248,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcCurMalformed", .expectedErrorMessage = "Source currency is malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsCurrencyNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -268,13 +268,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstAmtMalformed", .expectedErrorMessage = "Destination amount/currency/issuer is malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysInvalidCurrency", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -288,13 +288,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcCurMalformed", .expectedErrorMessage = "Source currency is malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsInvalidCurrency", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -308,13 +308,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstAmtMalformed", .expectedErrorMessage = "Destination amount/currency/issuer is malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysMissingIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -327,13 +327,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Invalid field 'taker_pays.issuer', expected non-XRP issuer." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsMissingIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -346,13 +346,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Invalid field 'taker_gets.issuer', expected non-XRP issuer." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysIssuerNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -366,13 +366,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "takerPaysIssuerNotString" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsIssuerNotString", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -386,13 +386,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "taker_gets.issuer should be string" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysInvalidIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -406,13 +406,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Source issuer is malformed." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsInvalidIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -426,13 +426,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Invalid field 'taker_gets.issuer', bad issuer." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerGetsXRPHasIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -447,13 +447,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "dstIsrMalformed", .expectedErrorMessage = "Unneeded field 'taker_gets.issuer' for XRP currency specification." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemTakerPaysXRPHasIssuer", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -468,13 +468,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "srcIsrMalformed", .expectedErrorMessage = "Unneeded field 'taker_pays.issuer' for XRP currency specification." }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemBadMartket", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -487,13 +487,13 @@ generateTestValuesForParametersTest() } } ] - })", + })JSON", .expectedError = "badMarket", .expectedErrorMessage = "badMarket" }, UnsubscribeParamTestCaseBundle{ .testName = "BooksItemInvalidBoth", - .testJson = R"({ + .testJson = R"JSON({ "books": [ { @@ -508,25 +508,25 @@ generateTestValuesForParametersTest() "both": 0 } ] - })", + })JSON", .expectedError = "invalidParams", .expectedErrorMessage = "bothNotBool" }, UnsubscribeParamTestCaseBundle{ .testName = "StreamPeerStatusNotSupport", - .testJson = R"({"streams": ["peer_status"]})", + .testJson = R"JSON({"streams": ["peer_status"]})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Operation not supported." }, UnsubscribeParamTestCaseBundle{ .testName = "StreamConsensusNotSupport", - .testJson = R"({"streams": ["consensus"]})", + .testJson = R"JSON({"streams": ["consensus"]})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Operation not supported." }, UnsubscribeParamTestCaseBundle{ .testName = "StreamServerNotSupport", - .testJson = R"({"streams": ["server"]})", + .testJson = R"JSON({"streams": ["server"]})JSON", .expectedError = "notSupported", .expectedErrorMessage = "Operation not supported." }, @@ -558,7 +558,7 @@ TEST_F(RPCUnsubscribeTest, EmptyResponse) { runSpawn([&, this](auto yield) { auto const handler = AnyHandler{UnsubscribeHandler{mockSubscriptionManagerPtr_}}; - auto const output = handler.process(json::parse(R"({})"), Context{yield, session_}); + auto const output = handler.process(json::parse(R"JSON({})JSON"), Context{yield, session_}); ASSERT_TRUE(output); EXPECT_TRUE(output.result->as_object().empty()); }); @@ -567,9 +567,9 @@ TEST_F(RPCUnsubscribeTest, EmptyResponse) TEST_F(RPCUnsubscribeTest, Streams) { auto const input = json::parse( - R"({ + R"JSON({ "streams": ["transactions_proposed","transactions","validations","manifests","book_changes","ledger"] - })" + })JSON" ); EXPECT_CALL(*mockSubscriptionManagerPtr_, unsubLedger).Times(1); @@ -590,9 +590,9 @@ TEST_F(RPCUnsubscribeTest, Streams) TEST_F(RPCUnsubscribeTest, Accounts) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "accounts": ["{}","{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2 )); @@ -612,9 +612,9 @@ TEST_F(RPCUnsubscribeTest, Accounts) TEST_F(RPCUnsubscribeTest, AccountsProposed) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "accounts_proposed": ["{}","{}"] - }})", + }})JSON", kACCOUNT, kACCOUNT2 )); @@ -635,7 +635,7 @@ TEST_F(RPCUnsubscribeTest, AccountsProposed) TEST_F(RPCUnsubscribeTest, Books) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "books": [ {{ "taker_pays": {{ @@ -648,7 +648,7 @@ TEST_F(RPCUnsubscribeTest, Books) "both": true }} ] - }})", + }})JSON", kACCOUNT )); @@ -669,7 +669,7 @@ TEST_F(RPCUnsubscribeTest, Books) TEST_F(RPCUnsubscribeTest, SingleBooks) { auto const input = json::parse(fmt::format( - R"({{ + R"JSON({{ "books": [ {{ "taker_pays": {{ @@ -681,7 +681,7 @@ TEST_F(RPCUnsubscribeTest, SingleBooks) }} }} ] - }})", + }})JSON", kACCOUNT )); diff --git a/tests/unit/rpc/handlers/VersionHandlerTests.cpp b/tests/unit/rpc/handlers/VersionHandlerTests.cpp index dcd8e7c75..829426c4e 100644 --- a/tests/unit/rpc/handlers/VersionHandlerTests.cpp +++ b/tests/unit/rpc/handlers/VersionHandlerTests.cpp @@ -53,11 +53,11 @@ TEST_F(RPCVersionHandlerTest, Default) }; boost::json::value jsonData = boost::json::parse(fmt::format( - R"({{ + R"JSON({{ "api_version.min": {}, "api_version.max": {}, "api_version.default": {} - }})", + }})JSON", kMIN_API_VERSION, kMAX_API_VERSION, kDEFAULT_API_VERSION diff --git a/tests/unit/util/config/ClioConfigDefinitionTests.cpp b/tests/unit/util/config/ClioConfigDefinitionTests.cpp index 3db94f745..279e8ca08 100644 --- a/tests/unit/util/config/ClioConfigDefinitionTests.cpp +++ b/tests/unit/util/config/ClioConfigDefinitionTests.cpp @@ -336,9 +336,9 @@ struct ClioConfigDefinitionParseArrayTest : NoLoggerFixture { TEST_F(ClioConfigDefinitionParseArrayTest, emptyArray) { - auto const configJson = boost::json::parse(R"json({ + auto const configJson = boost::json::parse(R"JSON({ "array": [] - })json") + })JSON") .as_object(); auto const result = config.parse(ConfigFileJson{configJson}); @@ -355,12 +355,12 @@ TEST_F(ClioConfigDefinitionParseArrayTest, emptyJson) TEST_F(ClioConfigDefinitionParseArrayTest, fullArray) { - auto const configJson = boost::json::parse(R"json({ + auto const configJson = boost::json::parse(R"JSON({ "array": [ {"int": 1, "string": "one"}, {"int": 2, "string": "two"} ] - })json") + })JSON") .as_object(); auto const result = config.parse(ConfigFileJson{configJson}); @@ -370,12 +370,12 @@ TEST_F(ClioConfigDefinitionParseArrayTest, fullArray) TEST_F(ClioConfigDefinitionParseArrayTest, onlyRequiredFields) { - auto const configJson = boost::json::parse(R"json({ + auto const configJson = boost::json::parse(R"JSON({ "array": [ {"int": 1}, {"int": 2} ] - })json") + })JSON") .as_object(); auto const configFile = ConfigFileJson{configJson}; @@ -391,12 +391,12 @@ TEST_F(ClioConfigDefinitionParseArrayTest, onlyRequiredFields) TEST_F(ClioConfigDefinitionParseArrayTest, someOptionalFieldsMissing) { - auto const configJson = boost::json::parse(R"json({ + auto const configJson = boost::json::parse(R"JSON({ "array": [ {"int": 1, "string": "one"}, {"int": 2} ] - })json") + })JSON") .as_object(); auto const configFile = ConfigFileJson{configJson}; @@ -412,12 +412,12 @@ TEST_F(ClioConfigDefinitionParseArrayTest, someOptionalFieldsMissing) TEST_F(ClioConfigDefinitionParseArrayTest, optionalFieldMissingAtFirstPosition) { - auto const configJson = boost::json::parse(R"json({ + auto const configJson = boost::json::parse(R"JSON({ "array": [ {"int": 1}, {"int": 2, "string": "two"} ] - })json") + })JSON") .as_object(); auto const configFile = ConfigFileJson{configJson}; @@ -434,12 +434,12 @@ TEST_F(ClioConfigDefinitionParseArrayTest, optionalFieldMissingAtFirstPosition) TEST_F(ClioConfigDefinitionParseArrayTest, missingRequiredFields) { - auto const configJson = boost::json::parse(R"json({ + auto const configJson = boost::json::parse(R"JSON({ "array": [ {"int": 1}, {"string": "two"} ] - })json") + })JSON") .as_object(); auto const configFile = ConfigFileJson{configJson}; @@ -451,12 +451,12 @@ TEST_F(ClioConfigDefinitionParseArrayTest, missingRequiredFields) TEST_F(ClioConfigDefinitionParseArrayTest, missingAllRequiredFields) { - auto const configJson = boost::json::parse(R"json({ + auto const configJson = boost::json::parse(R"JSON({ "array": [ {"string": "one"}, {"string": "two"} ] - })json") + })JSON") .as_object(); auto const configFile = ConfigFileJson{configJson}; diff --git a/tests/unit/util/config/ConfigFileJsonTests.cpp b/tests/unit/util/config/ConfigFileJsonTests.cpp index 0aaf438d8..cb6d2feb6 100644 --- a/tests/unit/util/config/ConfigFileJsonTests.cpp +++ b/tests/unit/util/config/ConfigFileJsonTests.cpp @@ -104,17 +104,17 @@ INSTANTIATE_TEST_CASE_P( testing::Values( ConfigFileJsonParseTestBundle{ .testName = "values", - .configStr = R"json({ + .configStr = R"JSON({ "int": 42, "double": 123.456, "bool": true, "string": "some string" - })json", + })JSON", .validationMap = {{"int", 42}, {"double", 123.456}, {"bool", true}, {"string", "some string"}} }, ConfigFileJsonParseTestBundle{ .testName = "nested", - .configStr = R"json({ + .configStr = R"JSON({ "level_0": { "int": 42, "level_1":{ @@ -127,7 +127,7 @@ INSTANTIATE_TEST_CASE_P( } } } - })json", + })JSON", .validationMap = {{"level_0.int", 42}, {"level_0.level_1.double", 123.456}, @@ -136,14 +136,14 @@ INSTANTIATE_TEST_CASE_P( }, ConfigFileJsonParseTestBundle{ .testName = "array", - .configStr = R"json({ + .configStr = R"JSON({ "array": [1, 2, 3] - })json", + })JSON", .validationMap = {{"array.[]", boost::json::array{1, 2, 3}}} }, ConfigFileJsonParseTestBundle{ .testName = "nested_array", - .configStr = R"json({ + .configStr = R"JSON({ "level_0": { "array": [1, 2, 3], "level_1": { @@ -153,7 +153,7 @@ INSTANTIATE_TEST_CASE_P( } } } - })json", + })JSON", .validationMap = { {"level_0.array.[]", boost::json::array{1, 2, 3}}, @@ -163,7 +163,7 @@ INSTANTIATE_TEST_CASE_P( }, ConfigFileJsonParseTestBundle{ .testName = "mixed", - .configStr = R"json({ + .configStr = R"JSON({ "int": 42, "double": 123.456, "bool": true, @@ -176,7 +176,7 @@ INSTANTIATE_TEST_CASE_P( "string": "some string", "array": [1, 2, 3] } - })json", + })JSON", .validationMap = { {"int", 42}, @@ -191,10 +191,10 @@ INSTANTIATE_TEST_CASE_P( {"nested.array.[]", boost::json::array{1, 2, 3}}, } }, - ConfigFileJsonParseTestBundle{.testName = "empty", .configStr = R"json({})json", .validationMap = {}}, + ConfigFileJsonParseTestBundle{.testName = "empty", .configStr = R"JSON({})JSON", .validationMap = {}}, ConfigFileJsonParseTestBundle{ .testName = "empty_nested", - .configStr = R"json({ + .configStr = R"JSON({ "level_0": { "level_1": { "level_2": { @@ -202,19 +202,19 @@ INSTANTIATE_TEST_CASE_P( } } } - })json", + })JSON", .validationMap = {} }, ConfigFileJsonParseTestBundle{ .testName = "empty_array", - .configStr = R"json({ + .configStr = R"JSON({ "array": [] - })json", + })JSON", .validationMap = {{"array.[]", boost::json::array{}}} }, ConfigFileJsonParseTestBundle{ .testName = "empty_nested_array", - .configStr = R"json({ + .configStr = R"JSON({ "level_0": { "array": [], "level_1": { @@ -224,7 +224,7 @@ INSTANTIATE_TEST_CASE_P( } } } - })json", + })JSON", .validationMap = { {"level_0.array.[]", boost::json::array{}}, @@ -234,72 +234,72 @@ INSTANTIATE_TEST_CASE_P( }, ConfigFileJsonParseTestBundle{ .testName = "object_inside_array", - .configStr = R"json({ + .configStr = R"JSON({ "array": [ { "int": 42 } ] - })json", + })JSON", .validationMap = {{"array.[].int", boost::json::array{42}}} }, ConfigFileJsonParseTestBundle{ .testName = "object_with_optional_fields_inside_array", - .configStr = R"json({ + .configStr = R"JSON({ "array": [ {"int": 42}, {"int": 24, "bool": true} ] - })json", + })JSON", .validationMap = {{"array.[].int", boost::json::array{42, 24}}, {"array.[].bool", boost::json::array{boost::json::value{}, true}}} }, ConfigFileJsonParseTestBundle{ .testName = "full_object_is_at_the_front_of_array", - .configStr = R"json({ + .configStr = R"JSON({ "array": [ {"int": 42, "bool": true}, {"int": 2}, {"int": 4} ] - })json", + })JSON", .validationMap = {{"array.[].int", boost::json::array{42, 2, 4}}, {"array.[].bool", boost::json::array{true, boost::json::value{}, boost::json::value{}}}} }, ConfigFileJsonParseTestBundle{ .testName = "full_object_is_in_the_middle_of_array", - .configStr = R"json({ + .configStr = R"JSON({ "array": [ {"int": 42}, {"int": 2, "bool": true}, {"int": 4} ] - })json", + })JSON", .validationMap = {{"array.[].int", boost::json::array{42, 2, 4}}, {"array.[].bool", boost::json::array{boost::json::value{}, true, boost::json::value{}}}} }, ConfigFileJsonParseTestBundle{ .testName = "no_full_object", - .configStr = R"json({ + .configStr = R"JSON({ "array": [ {"int": 42}, {"int": 2}, {"bool": true} ] - })json", + })JSON", .validationMap = {{"array.[].int", boost::json::array{42, 2, boost::json::value{}}}, {"array.[].bool", boost::json::array{boost::json::value{}, boost::json::value{}, true}}} }, ConfigFileJsonParseTestBundle{ .testName = "array_with_nexted_objects", - .configStr = R"json({ + .configStr = R"JSON({ "array": [ { "object": { "int": 42 } }, { "object": { "string": "some string" } } ] - })json", + })JSON", .validationMap = {{"array.[].object.int", boost::json::array{42, boost::json::value{}}}, {"array.[].object.string", boost::json::array{boost::json::value{}, "some string"}}} @@ -312,12 +312,12 @@ struct ConfigFileJsonTest : NoLoggerFixture {}; TEST_F(ConfigFileJsonTest, getValue) { - auto const jsonStr = R"json({ + auto const jsonStr = R"JSON({ "int": 42, "object": { "string": "some string" }, "bool": true, "double": 123.456 - })json"; + })JSON"; auto const jsonFileObj = ConfigFileJson{boost::json::parse(jsonStr).as_object()}; auto const intValue = jsonFileObj.getValue("int"); @@ -349,28 +349,28 @@ TEST_F(ConfigFileJsonAssertTest, getValueInvalidKey) TEST_F(ConfigFileJsonAssertTest, getValueOfArray) { - auto const jsonStr = R"json({ + auto const jsonStr = R"JSON({ "array": [1, 2, 3] - })json"; + })JSON"; auto const jsonFileObj = ConfigFileJson{boost::json::parse(jsonStr).as_object()}; EXPECT_CLIO_ASSERT_FAIL([[maybe_unused]] auto a = jsonFileObj.getValue("array")); } TEST_F(ConfigFileJsonAssertTest, nullIsNotSupported) { - auto const jsonStr = R"json({ + auto const jsonStr = R"JSON({ "null": null - })json"; + })JSON"; auto const jsonFileObj = ConfigFileJson{boost::json::parse(jsonStr).as_object()}; EXPECT_CLIO_ASSERT_FAIL([[maybe_unused]] auto a = jsonFileObj.getValue("null")); } TEST_F(ConfigFileJsonTest, getArray) { - auto const jsonStr = R"json({ + auto const jsonStr = R"JSON({ "array": [1, "2", 3.14, true], "object": { "array": [3, 4] } - })json"; + })JSON"; auto const jsonFileObj = ConfigFileJson{boost::json::parse(jsonStr).as_object()}; auto const array = jsonFileObj.getArray("array.[]"); @@ -400,12 +400,12 @@ TEST_F(ConfigFileJsonTest, getArray) TEST_F(ConfigFileJsonTest, getArrayObjectInArray) { - auto const jsonStr = R"json({ + auto const jsonStr = R"JSON({ "array": [ { "int": 42 }, { "string": "some string" } ] - })json"; + })JSON"; auto const jsonFileObj = ConfigFileJson{boost::json::parse(jsonStr).as_object()}; auto const ints = jsonFileObj.getArray("array.[].int"); @@ -423,12 +423,12 @@ TEST_F(ConfigFileJsonTest, getArrayObjectInArray) TEST_F(ConfigFileJsonTest, getArrayOptionalInArray) { - auto const jsonStr = R"json({ + auto const jsonStr = R"JSON({ "array": [ { "int": 42 }, { "int": 24, "bool": true } ] - })json"; + })JSON"; auto const jsonFileObj = ConfigFileJson{boost::json::parse(jsonStr).as_object()}; auto const ints = jsonFileObj.getArray("array.[].int"); @@ -453,21 +453,21 @@ TEST_F(ConfigFileJsonAssertTest, getArrayInvalidKey) TEST_F(ConfigFileJsonAssertTest, getArrayNotArray) { - auto const jsonStr = R"json({ + auto const jsonStr = R"JSON({ "int": 42 - })json"; + })JSON"; auto const jsonFileObj = ConfigFileJson{boost::json::parse(jsonStr).as_object()}; EXPECT_CLIO_ASSERT_FAIL([[maybe_unused]] auto a = jsonFileObj.getArray("int")); } TEST_F(ConfigFileJsonTest, containsKey) { - auto const jsonStr = R"json({ + auto const jsonStr = R"JSON({ "int": 42, "object": { "string": "some string", "array": [1, 2, 3] }, "array2": [1, 2, 3], "array_of_objects": [ {"int": 42}, {"string": "some string"} ] - })json"; + })JSON"; auto const jsonFileObj = ConfigFileJson{boost::json::parse(jsonStr).as_object()}; EXPECT_TRUE(jsonFileObj.containsKey("int")); @@ -505,7 +505,7 @@ TEST_F(ConfigFileJsonMakeTest, invalidJson) TEST_F(ConfigFileJsonMakeTest, validFile) { - auto const file = TmpFile(R"json({ "int": 42 })json"); + auto const file = TmpFile(R"JSON({ "int": 42 })JSON"); auto const jsonFileObj = ConfigFileJson::makeConfigFileJson(file.path); ASSERT_TRUE(jsonFileObj.has_value()); diff --git a/tests/unit/util/prometheus/CounterTests.cpp b/tests/unit/util/prometheus/CounterTests.cpp index eb1bda3fc..85326f568 100644 --- a/tests/unit/util/prometheus/CounterTests.cpp +++ b/tests/unit/util/prometheus/CounterTests.cpp @@ -41,7 +41,7 @@ struct AnyCounterTests : virtual ::testing::Test { ::testing::StrictMock mockCounterImpl; std::string const name = "test_counter"; - std::string labelsString = R"({label1="value1",label2="value2"})"; + std::string labelsString = R"JSON({label1="value1",label2="value2"})JSON"; CounterInt counter{name, labelsString, static_cast(mockCounterImpl)}; }; diff --git a/tests/unit/util/prometheus/HistogramTests.cpp b/tests/unit/util/prometheus/HistogramTests.cpp index 82a0d4eb6..d7b638e15 100644 --- a/tests/unit/util/prometheus/HistogramTests.cpp +++ b/tests/unit/util/prometheus/HistogramTests.cpp @@ -44,7 +44,7 @@ struct AnyHistogramTests : ::testing::Test { ::testing::StrictMock mockHistogramImpl; std::string const name = "test_histogram"; - std::string labelsString = R"({label1="value1",label2="value2"})"; + std::string labelsString = R"JSON({label1="value1",label2="value2"})JSON"; HistogramInt histogram{name, labelsString, {1, 2, 3}, static_cast(mockHistogramImpl)}; }; @@ -72,7 +72,7 @@ TEST_F(AnyHistogramTests, serializeValue) } struct HistogramTests : ::testing::Test { - std::string labelsString = R"({label1="value1",label2="value2"})"; + std::string labelsString = R"JSON({label1="value1",label2="value2"})JSON"; HistogramInt histogram{"t", labelsString, {1, 2, 3}}; std::string diff --git a/tests/unit/util/prometheus/LabelTests.cpp b/tests/unit/util/prometheus/LabelTests.cpp index 99a9b2441..966502069 100644 --- a/tests/unit/util/prometheus/LabelTests.cpp +++ b/tests/unit/util/prometheus/LabelTests.cpp @@ -47,8 +47,9 @@ TEST(LabelTests, serialize) TEST(LabelsTest, serialize) { EXPECT_EQ(Labels().serialize(), ""); - EXPECT_EQ(Labels({Label("name", "value")}).serialize(), R"({name="value"})"); + EXPECT_EQ(Labels({Label("name", "value")}).serialize(), R"JSON({name="value"})JSON"); EXPECT_EQ( - Labels({Label("name", "value"), Label("name2", "value2")}).serialize(), R"({name="value",name2="value2"})" + Labels({Label("name", "value"), Label("name2", "value2")}).serialize(), + R"JSON({name="value",name2="value2"})JSON" ); } diff --git a/tests/unit/web/AdminVerificationTests.cpp b/tests/unit/web/AdminVerificationTests.cpp index 1e0e7c2db..dc88ec706 100644 --- a/tests/unit/web/AdminVerificationTests.cpp +++ b/tests/unit/web/AdminVerificationTests.cpp @@ -169,27 +169,27 @@ INSTANTIATE_TEST_SUITE_P( }, MakeAdminVerificationStrategyFromConfigTestParams{ .testName = "OnlyPassword", - .config = R"({"server": {"admin_password": "password"}})", + .config = R"JSON({"server": {"admin_password": "password"}})JSON", .expectedError = false }, MakeAdminVerificationStrategyFromConfigTestParams{ .testName = "OnlyLocalAdmin", - .config = R"({"server": {"local_admin": true}})", + .config = R"JSON({"server": {"local_admin": true}})JSON", .expectedError = false }, MakeAdminVerificationStrategyFromConfigTestParams{ .testName = "OnlyLocalAdminDisabled", - .config = R"({"server": {"local_admin": false}})", + .config = R"JSON({"server": {"local_admin": false}})JSON", .expectedError = true }, MakeAdminVerificationStrategyFromConfigTestParams{ .testName = "LocalAdminAndPassword", - .config = R"({"server": {"local_admin": true, "admin_password": "password"}})", + .config = R"JSON({"server": {"local_admin": true, "admin_password": "password"}})JSON", .expectedError = true }, MakeAdminVerificationStrategyFromConfigTestParams{ .testName = "LocalAdminDisabledAndPassword", - .config = R"({"server": {"local_admin": false, "admin_password": "password"}})", + .config = R"JSON({"server": {"local_admin": false, "admin_password": "password"}})JSON", .expectedError = false } ), diff --git a/tests/unit/web/RPCServerHandlerTests.cpp b/tests/unit/web/RPCServerHandlerTests.cpp index c36a7a53d..300d91a0d 100644 --- a/tests/unit/web/RPCServerHandlerTests.cpp +++ b/tests/unit/web/RPCServerHandlerTests.cpp @@ -112,15 +112,15 @@ struct WebRPCServerHandlerTest : util::prometheus::WithPrometheus, MockBackendTe TEST_F(WebRPCServerHandlerTest, HTTPDefaultPath) { - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "method": "server_info", "params": [{}] - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); static constexpr auto kRESULT = "{}"; - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "result": { "status": "success" }, @@ -130,7 +130,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPDefaultPath) "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -148,10 +148,10 @@ TEST_F(WebRPCServerHandlerTest, HTTPDefaultPath) TEST_F(WebRPCServerHandlerTest, HTTPRejectedByDosguard) { - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "method": "server_info", "params": [{}] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(false)); @@ -161,10 +161,10 @@ TEST_F(WebRPCServerHandlerTest, HTTPRejectedByDosguard) TEST_F(WebRPCServerHandlerTest, HTTPRejectedByDosguardAfterParsing) { - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "method": "server_info", "params": [{}] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(false)); @@ -176,16 +176,16 @@ TEST_F(WebRPCServerHandlerTest, HTTPRejectedByDosguardAfterParsing) TEST_F(WebRPCServerHandlerTest, WsNormalPath) { session->upgraded = true; - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command": "server_info", "id": 99, "api_version": 2 - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); static constexpr auto kRESULT = "{}"; - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "result":{}, "id": 99, "status": "success", @@ -197,7 +197,7 @@ TEST_F(WebRPCServerHandlerTest, WsNormalPath) "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) .WillOnce(testing::Return(true)); @@ -215,11 +215,11 @@ TEST_F(WebRPCServerHandlerTest, WsNormalPath) TEST_F(WebRPCServerHandlerTest, WsRejectedByDosguard) { session->upgraded = true; - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command": "server_info", "id": 99, "api_version": 2 - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(false)); @@ -230,11 +230,11 @@ TEST_F(WebRPCServerHandlerTest, WsRejectedByDosguard) TEST_F(WebRPCServerHandlerTest, WsRejectedByDosguardAfterParsing) { session->upgraded = true; - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command": "server_info", "id": 99, "api_version": 2 - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -246,21 +246,21 @@ TEST_F(WebRPCServerHandlerTest, WsRejectedByDosguardAfterParsing) TEST_F(WebRPCServerHandlerTest, HTTPForwardedPath) { - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "method": "server_info", "params": [{}] - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); // Note: forwarding always goes thru WS API - static constexpr auto kRESULT = R"({ + static constexpr auto kRESULT = R"JSON({ "result": { "index": 1 }, "forwarded": true - })"; - static constexpr auto kRESPONSE = R"({ + })JSON"; + static constexpr auto kRESPONSE = R"JSON({ "result":{ "index": 1, "status": "success" @@ -272,7 +272,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPForwardedPath) "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -290,23 +290,23 @@ TEST_F(WebRPCServerHandlerTest, HTTPForwardedPath) TEST_F(WebRPCServerHandlerTest, HTTPForwardedErrorPath) { - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "method": "server_info", "params": [{}] - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); // Note: forwarding always goes thru WS API - static constexpr auto kRESULT = R"({ + static constexpr auto kRESULT = R"JSON({ "error": "error", "error_code": 123, "error_message": "error message", "status": "error", "type": "response", "forwarded": true - })"; - static constexpr auto kRESPONSE = R"({ + })JSON"; + static constexpr auto kRESPONSE = R"JSON({ "result":{ "error": "error", "error_code": 123, @@ -321,7 +321,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPForwardedErrorPath) "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -340,21 +340,21 @@ TEST_F(WebRPCServerHandlerTest, HTTPForwardedErrorPath) TEST_F(WebRPCServerHandlerTest, WsForwardedPath) { session->upgraded = true; - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command": "server_info", "id": 99 - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); // Note: forwarding always goes thru WS API - static constexpr auto kRESULT = R"({ + static constexpr auto kRESULT = R"JSON({ "result": { "index": 1 }, "forwarded": true - })"; - static constexpr auto kRESPONSE = R"({ + })JSON"; + static constexpr auto kRESPONSE = R"JSON({ "result":{ "index": 1 }, @@ -368,7 +368,7 @@ TEST_F(WebRPCServerHandlerTest, WsForwardedPath) "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -387,24 +387,24 @@ TEST_F(WebRPCServerHandlerTest, WsForwardedPath) TEST_F(WebRPCServerHandlerTest, WsForwardedErrorPath) { session->upgraded = true; - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command": "server_info", "id": 99 - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); // Note: forwarding always goes thru WS API - static constexpr auto kRESULT = R"({ + static constexpr auto kRESULT = R"JSON({ "error": "error", "error_code": 123, "error_message": "error message", "status": "error", "type": "response", "forwarded": true - })"; + })JSON"; // WS error responses, unlike their successful counterpart, contain everything on top level without "result" - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "error": "error", "error_code": 123, "error_message": "error message", @@ -418,7 +418,7 @@ TEST_F(WebRPCServerHandlerTest, WsForwardedErrorPath) "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -437,7 +437,7 @@ TEST_F(WebRPCServerHandlerTest, WsForwardedErrorPath) TEST_F(WebRPCServerHandlerTest, HTTPErrorPath) { - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "result": { "error": "invalidParams", "error_code": 31, @@ -459,18 +459,18 @@ TEST_F(WebRPCServerHandlerTest, HTTPErrorPath) "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" } ] - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); - static constexpr auto kREQUEST_JSON = R"({ + static constexpr auto kREQUEST_JSON = R"JSON({ "method": "ledger", "params": [ { "ledger_index": "xx" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST_JSON).as_object())) @@ -489,7 +489,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPErrorPath) TEST_F(WebRPCServerHandlerTest, WsErrorPath) { session->upgraded = true; - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "id": "123", "error": "invalidParams", "error_code": 31, @@ -509,16 +509,16 @@ TEST_F(WebRPCServerHandlerTest, WsErrorPath) "message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request" } ] - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); - static constexpr auto kREQUEST_JSON = R"({ + static constexpr auto kREQUEST_JSON = R"JSON({ "command": "ledger", "ledger_index": "xx", "id": "123", "api_version": 2 - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST_JSON).as_object())) @@ -536,12 +536,12 @@ TEST_F(WebRPCServerHandlerTest, WsErrorPath) TEST_F(WebRPCServerHandlerTest, HTTPNotReady) { - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "method": "server_info", "params": [{}] - })"; + })JSON"; - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "result": { "error": "notReady", "error_code": 13, @@ -553,7 +553,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPNotReady) "params": [{}] } } - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -569,12 +569,12 @@ TEST_F(WebRPCServerHandlerTest, WsNotReady) { session->upgraded = true; - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command": "server_info", "id": 99 - })"; + })JSON"; - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "error": "notReady", "error_code": 13, "error_message": "Not ready to handle this request.", @@ -585,7 +585,7 @@ TEST_F(WebRPCServerHandlerTest, WsNotReady) "command": "server_info", "id": 99 } - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -599,11 +599,11 @@ TEST_F(WebRPCServerHandlerTest, WsNotReady) TEST_F(WebRPCServerHandlerTest, HTTPBadSyntaxWhenRequestSubscribe) { - static constexpr auto kREQUEST = R"({"method": "subscribe"})"; + static constexpr auto kREQUEST = R"JSON({"method": "subscribe"})JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "result": { "error": "badSyntax", "error_code": 1, @@ -615,7 +615,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPBadSyntaxWhenRequestSubscribe) "params": [{}] } } - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true)); @@ -628,7 +628,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPBadSyntaxWhenRequestSubscribe) TEST_F(WebRPCServerHandlerTest, HTTPMissingCommand) { - static constexpr auto kREQUEST = R"({"method2": "server_info"})"; + static constexpr auto kREQUEST = R"JSON({"method2": "server_info"})JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); @@ -646,7 +646,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPMissingCommand) TEST_F(WebRPCServerHandlerTest, HTTPCommandNotString) { - static constexpr auto kREQUEST = R"({"method": 1})"; + static constexpr auto kREQUEST = R"JSON({"method": 1})JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); @@ -664,7 +664,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPCommandNotString) TEST_F(WebRPCServerHandlerTest, HTTPCommandIsEmpty) { - static constexpr auto kREQUEST = R"({"method": ""})"; + static constexpr auto kREQUEST = R"JSON({"method": ""})JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); @@ -683,14 +683,14 @@ TEST_F(WebRPCServerHandlerTest, HTTPCommandIsEmpty) TEST_F(WebRPCServerHandlerTest, WsMissingCommand) { session->upgraded = true; - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command2": "server_info", "id": 99 - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "error": "missingCommand", "error_code": 6001, "error_message": "Method/Command is not specified or is not a string.", @@ -701,7 +701,7 @@ TEST_F(WebRPCServerHandlerTest, WsMissingCommand) "command2": "server_info", "id": 99 } - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -719,10 +719,10 @@ TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparsableNotArray) backend_->setRange(kMIN_SEQ, kMAX_SEQ); - static constexpr auto kREQUEST_JSON = R"({ + static constexpr auto kREQUEST_JSON = R"JSON({ "method": "ledger", "params": "wrong" - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true)); @@ -740,10 +740,10 @@ TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparsableArrayWithDigit) backend_->setRange(kMIN_SEQ, kMAX_SEQ); - static constexpr auto kREQUEST_JSON = R"({ + static constexpr auto kREQUEST_JSON = R"JSON({ "method": "ledger", "params": [1] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true)); @@ -757,7 +757,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparsableArrayWithDigit) TEST_F(WebRPCServerHandlerTest, HTTPInternalError) { - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "result": { "error": "internal", "error_code": 73, @@ -769,14 +769,14 @@ TEST_F(WebRPCServerHandlerTest, HTTPInternalError) "params": [{}] } } - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); - static constexpr auto kREQUEST_JSON = R"({ + static constexpr auto kREQUEST_JSON = R"JSON({ "method": "ledger", "params": [{}] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST_JSON).as_object())) @@ -793,7 +793,7 @@ TEST_F(WebRPCServerHandlerTest, WsInternalError) { session->upgraded = true; - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "error": "internal", "error_code": 73, "error_message": "Internal error.", @@ -804,14 +804,14 @@ TEST_F(WebRPCServerHandlerTest, WsInternalError) "command": "ledger", "id": "123" } - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); - static constexpr auto kREQUEST_JSON = R"({ + static constexpr auto kREQUEST_JSON = R"JSON({ "command": "ledger", "id": "123" - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST_JSON).as_object())) @@ -826,15 +826,15 @@ TEST_F(WebRPCServerHandlerTest, WsInternalError) TEST_F(WebRPCServerHandlerTest, HTTPOutDated) { - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "method": "server_info", "params": [{}] - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); static constexpr auto kRESULT = "{}"; - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "result": { "status": "success" }, @@ -848,7 +848,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPOutDated) "message": "This server may be out of date" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -868,15 +868,15 @@ TEST_F(WebRPCServerHandlerTest, WsOutdated) { session->upgraded = true; - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command": "server_info", "id": 99 - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); static constexpr auto kRESULT = "{}"; - static constexpr auto kRESPONSE = R"({ + static constexpr auto kRESPONSE = R"JSON({ "result":{}, "id": 99, "status": "success", @@ -891,7 +891,7 @@ TEST_F(WebRPCServerHandlerTest, WsOutdated) "message": "This server may be out of date" } ] - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -913,21 +913,21 @@ TEST_F(WebRPCServerHandlerTest, WsTooBusy) auto localRpcEngine = std::make_shared(); auto localHandler = std::make_shared>(cfg, backend_, localRpcEngine, etl, dosguard); - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "command": "server_info", "id": 99 - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); static constexpr auto kRESPONSE = - R"({ + R"JSON({ "error": "tooBusy", "error_code": 9, "error_message": "The server is too busy to help you now.", "status": "error", "type": "response" - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -944,21 +944,21 @@ TEST_F(WebRPCServerHandlerTest, HTTPTooBusy) { auto localRpcEngine = std::make_shared(); auto localHandler = std::make_shared>(cfg, backend_, localRpcEngine, etl, dosguard); - static constexpr auto kREQUEST = R"({ + static constexpr auto kREQUEST = R"JSON({ "method": "server_info", "params": [{}] - })"; + })JSON"; backend_->setRange(kMIN_SEQ, kMAX_SEQ); static constexpr auto kRESPONSE = - R"({ + R"JSON({ "error": "tooBusy", "error_code": 9, "error_message": "The server is too busy to help you now.", "status": "error", "type": "response" - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object())) @@ -990,13 +990,13 @@ TEST_F(WebRPCServerHandlerTest, WsRequestNotJson) session->upgraded = true; static constexpr auto kREQUEST = "not json"; static constexpr auto kRESPONSE = - R"({ + R"JSON({ "error": "badSyntax", "error_code": 1, "error_message": "Syntax error.", "status": "error", "type": "response" - })"; + })JSON"; EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true)); @@ -1045,12 +1045,12 @@ INSTANTIATE_TEST_CASE_P( TEST_P(WebRPCServerHandlerInvalidAPIVersionParamTest, HTTPInvalidAPIVersion) { auto request = fmt::format( - R"({{ + R"JSON({{ "method": "server_info", "params": [{{ "api_version": {} }}] - }})", + }})JSON", GetParam().version ); @@ -1071,10 +1071,10 @@ TEST_P(WebRPCServerHandlerInvalidAPIVersionParamTest, WSInvalidAPIVersion) { session->upgraded = true; auto request = fmt::format( - R"({{ + R"JSON({{ "method": "server_info", "api_version": {} - }})", + }})JSON", GetParam().version ); diff --git a/tests/unit/web/ServerTests.cpp b/tests/unit/web/ServerTests.cpp index 3f8b665ed..630da8595 100644 --- a/tests/unit/web/ServerTests.cpp +++ b/tests/unit/web/ServerTests.cpp @@ -246,8 +246,8 @@ TEST_F(WebServerTest, Http) { auto const e = std::make_shared(); auto const server = makeServerSync(cfg, ctx, dosGuard, e); - auto const [status, res] = HttpSyncClient::post("localhost", port, R"({"Hello":1})"); - EXPECT_EQ(res, R"({"Hello":1})"); + auto const [status, res] = HttpSyncClient::post("localhost", port, R"JSON({"Hello":1})JSON"); + EXPECT_EQ(res, R"JSON({"Hello":1})JSON"); EXPECT_EQ(status, boost::beast::http::status::ok); } @@ -257,8 +257,8 @@ TEST_F(WebServerTest, Ws) auto const server = makeServerSync(cfg, ctx, dosGuard, e); WebSocketSyncClient wsClient; wsClient.connect("localhost", port); - auto const res = wsClient.syncPost(R"({"Hello":1})"); - EXPECT_EQ(res, R"({"Hello":1})"); + auto const res = wsClient.syncPost(R"JSON({"Hello":1})JSON"); + EXPECT_EQ(res, R"JSON({"Hello":1})JSON"); wsClient.disconnect(); } @@ -266,10 +266,10 @@ TEST_F(WebServerTest, HttpInternalError) { auto const e = std::make_shared(); auto const server = makeServerSync(cfg, ctx, dosGuard, e); - auto const [status, res] = HttpSyncClient::post("localhost", port, R"({})"); + auto const [status, res] = HttpSyncClient::post("localhost", port, R"JSON({})JSON"); EXPECT_EQ( res, - R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})" + R"JSON({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})JSON" ); EXPECT_EQ(status, boost::beast::http::status::internal_server_error); } @@ -280,11 +280,11 @@ TEST_F(WebServerTest, WsInternalError) auto const server = makeServerSync(cfg, ctx, dosGuard, e); WebSocketSyncClient wsClient; wsClient.connect("localhost", port); - auto const res = wsClient.syncPost(R"({"id":"id1"})"); + auto const res = wsClient.syncPost(R"JSON({"id":"id1"})JSON"); wsClient.disconnect(); EXPECT_EQ( res, - R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","id":"id1","request":{"id":"id1"}})" + R"JSON({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","id":"id1","request":{"id":"id1"}})JSON" ); } @@ -298,7 +298,7 @@ TEST_F(WebServerTest, WsInternalErrorNotJson) wsClient.disconnect(); EXPECT_EQ( res, - R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","request":"not json"})" + R"JSON({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","request":"not json"})JSON" ); } @@ -330,8 +330,8 @@ TEST_F(WebServerTest, Https) auto const e = std::make_shared(); cfg = getParseServerConfig(addSslConfig(generateJSONWithDynamicPort(port))); auto const server = makeServerSync(cfg, ctx, dosGuard, e); - auto const res = HttpsSyncClient::syncPost("localhost", port, R"({"Hello":1})"); - EXPECT_EQ(res, R"({"Hello":1})"); + auto const res = HttpsSyncClient::syncPost("localhost", port, R"JSON({"Hello":1})JSON"); + EXPECT_EQ(res, R"JSON({"Hello":1})JSON"); } TEST_F(WebServerTest, Wss) @@ -341,8 +341,8 @@ TEST_F(WebServerTest, Wss) auto server = makeServerSync(cfg, ctx, dosGuard, e); WebServerSslSyncClient wsClient; wsClient.connect("localhost", port); - auto const res = wsClient.syncPost(R"({"Hello":1})"); - EXPECT_EQ(res, R"({"Hello":1})"); + auto const res = wsClient.syncPost(R"JSON({"Hello":1})JSON"); + EXPECT_EQ(res, R"JSON({"Hello":1})JSON"); wsClient.disconnect(); } @@ -351,10 +351,11 @@ TEST_F(WebServerTest, HttpPayloadOverload) std::string const s100(100, 'a'); auto const e = std::make_shared(); auto server = makeServerSync(cfg, ctx, dosGuardOverload, e); - auto const [status, res] = HttpSyncClient::post("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100)); + auto const [status, res] = + HttpSyncClient::post("localhost", port, fmt::format(R"JSON({{"payload":"{}"}})JSON", s100)); EXPECT_EQ( res, - R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})" + R"JSON({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})JSON" ); EXPECT_EQ(status, boost::beast::http::status::ok); } @@ -366,11 +367,11 @@ TEST_F(WebServerTest, WsPayloadOverload) auto server = makeServerSync(cfg, ctx, dosGuardOverload, e); WebSocketSyncClient wsClient; wsClient.connect("localhost", port); - auto const res = wsClient.syncPost(fmt::format(R"({{"payload":"{}"}})", s100)); + auto const res = wsClient.syncPost(fmt::format(R"JSON({{"payload":"{}"}})JSON", s100)); wsClient.disconnect(); EXPECT_EQ( res, - R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})" + R"JSON({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})JSON" ); } diff --git a/tests/unit/web/dosguard/WeightsTests.cpp b/tests/unit/web/dosguard/WeightsTests.cpp index 5c80fe7bd..5b585a718 100644 --- a/tests/unit/web/dosguard/WeightsTests.cpp +++ b/tests/unit/web/dosguard/WeightsTests.cpp @@ -66,133 +66,133 @@ INSTANTIATE_TEST_SUITE_P( WeightsTest, ::testing::Values( TestParams{.testName = "EmptyObject", .requestJson = "{}", .expectedWeight = 10}, - TestParams{.testName = "NonStringMethod", .requestJson = R"json({"method": 123})json", .expectedWeight = 10}, - TestParams{.testName = "NonStringCommand", .requestJson = R"json({"command": 123})json", .expectedWeight = 10}, + TestParams{.testName = "NonStringMethod", .requestJson = R"JSON({"method": 123})JSON", .expectedWeight = 10}, + TestParams{.testName = "NonStringCommand", .requestJson = R"JSON({"command": 123})JSON", .expectedWeight = 10}, TestParams{ .testName = "UnknownMethodName", - .requestJson = R"json({"method": "unknown_method"})json", + .requestJson = R"JSON({"method": "unknown_method"})JSON", .expectedWeight = 10 }, TestParams{ .testName = "UnknownCommandName", - .requestJson = R"json({"command": "unknown_command"})json", + .requestJson = R"JSON({"command": "unknown_command"})JSON", .expectedWeight = 10 }, TestParams{ .testName = "OnlyWeight_NoLedgerIndex", - .requestJson = R"json({"method": "only_weight"})json", + .requestJson = R"JSON({"method": "only_weight"})JSON", .expectedWeight = 20 }, TestParams{ .testName = "OnlyWeight_CurrentLedgerIndex", - .requestJson = R"json({"method": "only_weight", "ledger_index": "current"})json", + .requestJson = R"JSON({"method": "only_weight", "ledger_index": "current"})JSON", .expectedWeight = 20 }, TestParams{ .testName = "OnlyWeight_ValidatedLedgerIndex", - .requestJson = R"json({"method": "only_weight", "ledger_index": "validated"})json", + .requestJson = R"JSON({"method": "only_weight", "ledger_index": "validated"})JSON", .expectedWeight = 20 }, TestParams{ .testName = "OnlyWeight_ClosedLedgerIndex", - .requestJson = R"json({"method": "only_weight", "ledger_index": "closed"})json", + .requestJson = R"JSON({"method": "only_weight", "ledger_index": "closed"})JSON", .expectedWeight = 20 }, TestParams{ .testName = "OnlyWeight_NumericLedgerIndex", - .requestJson = R"json({"method": "only_weight", "ledger_index": "123"})json", + .requestJson = R"JSON({"method": "only_weight", "ledger_index": "123"})JSON", .expectedWeight = 20 }, TestParams{ .testName = "OnlyWeight_OtherStringLedgerIndex", - .requestJson = R"json({"method": "only_weight", "ledger_index": "some_string"})json", + .requestJson = R"JSON({"method": "only_weight", "ledger_index": "some_string"})JSON", .expectedWeight = 20 }, // With Current Weight TestParams{ .testName = "WithCurrentWeight_NoLedgerIndex", - .requestJson = R"json({"method": "with_current_weight"})json", + .requestJson = R"JSON({"method": "with_current_weight"})JSON", .expectedWeight = 30 }, TestParams{ .testName = "WithCurrentWeight_CurrentLedgerIndex", - .requestJson = R"json({"method": "with_current_weight", "ledger_index": "current"})json", + .requestJson = R"JSON({"method": "with_current_weight", "ledger_index": "current"})JSON", .expectedWeight = 35 }, TestParams{ .testName = "WithCurrentWeight_ValidatedLedgerIndex", - .requestJson = R"json({"method": "with_current_weight", "ledger_index": "validated"})json", + .requestJson = R"JSON({"method": "with_current_weight", "ledger_index": "validated"})JSON", .expectedWeight = 30 }, // With Validated Weight TestParams{ .testName = "WithValidatedWeight_NoLedgerIndex", - .requestJson = R"json({"method": "with_validated_weight"})json", + .requestJson = R"JSON({"method": "with_validated_weight"})JSON", .expectedWeight = 40 }, TestParams{ .testName = "WithValidatedWeight_CurrentLedgerIndex", - .requestJson = R"json({"method": "with_validated_weight", "ledger_index": "current"})json", + .requestJson = R"JSON({"method": "with_validated_weight", "ledger_index": "current"})JSON", .expectedWeight = 40 }, TestParams{ .testName = "WithValidatedWeight_ValidatedLedgerIndex", - .requestJson = R"json({"method": "with_validated_weight", "ledger_index": "validated"})json", + .requestJson = R"JSON({"method": "with_validated_weight", "ledger_index": "validated"})JSON", .expectedWeight = 45 }, // With Both Weights TestParams{ .testName = "WithBothWeights_NoLedgerIndex", - .requestJson = R"json({"method": "with_both_weights"})json", + .requestJson = R"JSON({"method": "with_both_weights"})JSON", .expectedWeight = 50 }, TestParams{ .testName = "WithBothWeights_CurrentLedgerIndex", - .requestJson = R"json({"method": "with_both_weights", "ledger_index": "current"})json", + .requestJson = R"JSON({"method": "with_both_weights", "ledger_index": "current"})JSON", .expectedWeight = 55 }, TestParams{ .testName = "WithBothWeights_ValidatedLedgerIndex", - .requestJson = R"json({"method": "with_both_weights", "ledger_index": "validated"})json", + .requestJson = R"JSON({"method": "with_both_weights", "ledger_index": "validated"})JSON", .expectedWeight = 60 }, // Using Command TestParams{ .testName = "UsingCommand_NoLedgerIndex", - .requestJson = R"json({"command": "with_both_weights"})json", + .requestJson = R"JSON({"command": "with_both_weights"})JSON", .expectedWeight = 50 }, TestParams{ .testName = "UsingCommand_CurrentLedgerIndex", - .requestJson = R"json({"command": "with_both_weights", "ledger_index": "current"})json", + .requestJson = R"JSON({"command": "with_both_weights", "ledger_index": "current"})JSON", .expectedWeight = 55 }, TestParams{ .testName = "UsingCommand_ValidatedLedgerIndex", - .requestJson = R"json({"command": "with_both_weights", "ledger_index": "validated"})json", + .requestJson = R"JSON({"command": "with_both_weights", "ledger_index": "validated"})JSON", .expectedWeight = 60 }, // With Params Array TestParams{ .testName = "WithParamsArray_CurrentLedgerIndex", - .requestJson = R"json({"method": "with_both_weights", "params": [{"ledger_index": "current"}]})json", + .requestJson = R"JSON({"method": "with_both_weights", "params": [{"ledger_index": "current"}]})JSON", .expectedWeight = 55 }, TestParams{ .testName = "WithParamsArray_ValidatedLedgerIndex", - .requestJson = R"json({"method": "with_both_weights", "params": [{"ledger_index": "validated"}]})json", + .requestJson = R"JSON({"method": "with_both_weights", "params": [{"ledger_index": "validated"}]})JSON", .expectedWeight = 60 }, TestParams{ .testName = "WithParamsArray_WithCommand", - .requestJson = R"json({"command": "with_both_weights", "params": [{"ledger_index": "current"}]})json", + .requestJson = R"JSON({"command": "with_both_weights", "params": [{"ledger_index": "current"}]})JSON", .expectedWeight = 55 } ), @@ -213,7 +213,7 @@ TEST(WeightsMakeTest, CreateFromConfig) {"dos_guard.__ng_weights.[].weight_ledger_validated", util::config::Array{util::config::ConfigValue{util::config::ConfigType::Integer}.optional()}} }; - std::string const configStr = R"json( + std::string const configStr = R"JSON( { "dos_guard": { "__ng_default_weight": 15, @@ -237,7 +237,7 @@ TEST(WeightsMakeTest, CreateFromConfig) ] } } - )json"; + )JSON"; auto const configJson = boost::json::parse(configStr).as_object(); @@ -245,33 +245,33 @@ TEST(WeightsMakeTest, CreateFromConfig) Weights const weights = Weights::make(mockConfig); - auto request = boost::json::parse(R"json({"method": "unknown_method"})json").as_object(); + auto request = boost::json::parse(R"JSON({"method": "unknown_method"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 15); - request = boost::json::parse(R"json({"method": "method1"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method1"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 25); - request = boost::json::parse(R"json({"method": "method1", "ledger_index": "current"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method1", "ledger_index": "current"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 30); - request = boost::json::parse(R"json({"method": "method1", "ledger_index": "validated"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method1", "ledger_index": "validated"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 25); - request = boost::json::parse(R"json({"method": "method2"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method2"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 35); - request = boost::json::parse(R"json({"method": "method2", "ledger_index": "current"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method2", "ledger_index": "current"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 35); - request = boost::json::parse(R"json({"method": "method2", "ledger_index": "validated"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method2", "ledger_index": "validated"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 40); - request = boost::json::parse(R"json({"method": "method3"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method3"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 45); - request = boost::json::parse(R"json({"method": "method3", "ledger_index": "current"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method3", "ledger_index": "current"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 50); - request = boost::json::parse(R"json({"method": "method3", "ledger_index": "validated"})json").as_object(); + request = boost::json::parse(R"JSON({"method": "method3", "ledger_index": "validated"})JSON").as_object(); EXPECT_EQ(weights.requestWeight(request), 55); } diff --git a/tests/unit/web/impl/ErrorHandlingTests.cpp b/tests/unit/web/impl/ErrorHandlingTests.cpp index e4012a08c..e0e51bed1 100644 --- a/tests/unit/web/impl/ErrorHandlingTests.cpp +++ b/tests/unit/web/impl/ErrorHandlingTests.cpp @@ -152,7 +152,7 @@ INSTANTIATE_TEST_CASE_P( "UpgradedConnection", true, rpc::Status{rpc::RippledError::rpcTOO_BUSY}, - R"({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})", + R"JSON({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})JSON", boost::beast::http::status::ok }, ErrorHandlingSendErrorTestBundle{ @@ -194,7 +194,7 @@ INSTANTIATE_TEST_CASE_P( "NotUpgradedConnection_RippledError", false, rpc::Status{rpc::RippledError::rpcTOO_BUSY}, - R"({"result":{"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"}})", + R"JSON({"result":{"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"}})JSON", boost::beast::http::status::bad_request }, }), @@ -209,7 +209,7 @@ TEST_F(ErrorHandlingTests, sendInternalError) *connection_, send( std::string{ - R"({"result":{"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"}})" + R"JSON({"result":{"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"}})JSON" }, boost::beast::http::status::internal_server_error ) @@ -224,7 +224,7 @@ TEST_F(ErrorHandlingTests, sendNotReadyError) *connection_, send( std::string{ - R"({"result":{"error":"notReady","error_code":13,"error_message":"Not ready to handle this request.","status":"error","type":"response"}})" + R"JSON({"result":{"error":"notReady","error_code":13,"error_message":"Not ready to handle this request.","status":"error","type":"response"}})JSON" }, boost::beast::http::status::ok ) @@ -240,7 +240,7 @@ TEST_F(ErrorHandlingTests, sendTooBusyError_UpgradedConnection) *connection_, send( std::string{ - R"({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})" + R"JSON({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})JSON" }, boost::beast::http::status::ok ) @@ -256,7 +256,7 @@ TEST_F(ErrorHandlingTests, sendTooBusyError_NotUpgradedConnection) *connection_, send( std::string{ - R"({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})" + R"JSON({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})JSON" }, boost::beast::http::status::service_unavailable ) @@ -272,7 +272,7 @@ TEST_F(ErrorHandlingTests, sendJsonParsingError_UpgradedConnection) *connection_, send( std::string{ - R"({"error":"badSyntax","error_code":1,"error_message":"Syntax error.","status":"error","type":"response"})" + R"JSON({"error":"badSyntax","error_code":1,"error_message":"Syntax error.","status":"error","type":"response"})JSON" }, boost::beast::http::status::ok ) diff --git a/tests/unit/web/ng/RPCServerHandlerTests.cpp b/tests/unit/web/ng/RPCServerHandlerTests.cpp index 87f3ef7af..f9bd82609 100644 --- a/tests/unit/web/ng/RPCServerHandlerTests.cpp +++ b/tests/unit/web/ng/RPCServerHandlerTests.cpp @@ -131,7 +131,7 @@ TEST_F(NgRpcServerHandlerTest, DosguardRejectedWsRequest) TEST_F(NgRpcServerHandlerTest, DosguardRejectedWsJsonRequest) { runSpawn([&](boost::asio::yield_context yield) { - auto const requestStr = R"json({"request": "some message", "id": "some id"})json"; + auto const requestStr = R"JSON({"request": "some message", "id": "some id"})JSON"; auto const request = makeWsRequest(requestStr); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(false)); @@ -326,7 +326,7 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_BuildResponseFailed) { backend_->setRange(0, 1); runSpawn([&](boost::asio::yield_context yield) { - std::string const requestStr = R"json({"method":"some_method"})json"; + std::string const requestStr = R"JSON({"method":"some_method"})JSON"; auto const request = makeHttpRequest(requestStr); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true)); @@ -357,7 +357,7 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_BuildResponseThrewAnException) { backend_->setRange(0, 1); runSpawn([&](boost::asio::yield_context yield) { - std::string const requestStr = R"json({"method":"some_method"})json"; + std::string const requestStr = R"JSON({"method":"some_method"})JSON"; auto const request = makeHttpRequest(requestStr); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true)); @@ -383,7 +383,7 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_Successful_HttpRequest) { backend_->setRange(0, 1); runSpawn([&](boost::asio::yield_context yield) { - std::string const requestStr = R"json({"method":"some_method"})json"; + std::string const requestStr = R"JSON({"method":"some_method"})JSON"; auto const request = makeHttpRequest(requestStr); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true)); @@ -416,7 +416,7 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_OutdatedWarning) { backend_->setRange(0, 1); runSpawn([&](boost::asio::yield_context yield) { - std::string const requestStr = R"json({"method":"some_method"})json"; + std::string const requestStr = R"JSON({"method":"some_method"})JSON"; auto const request = makeHttpRequest(requestStr); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true)); @@ -455,7 +455,7 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_Successful_HttpRequest_Forwarded) { backend_->setRange(0, 1); runSpawn([&](boost::asio::yield_context yield) { - std::string const requestStr = R"json({"method":"some_method"})json"; + std::string const requestStr = R"JSON({"method":"some_method"})JSON"; auto const request = makeHttpRequest(requestStr); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true)); @@ -491,7 +491,7 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_Successful_HttpRequest_HasError) { backend_->setRange(0, 1); runSpawn([&](boost::asio::yield_context yield) { - std::string const requestStr = R"json({"method":"some_method"})json"; + std::string const requestStr = R"JSON({"method":"some_method"})JSON"; auto const request = makeHttpRequest(requestStr); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true)); @@ -543,7 +543,7 @@ TEST_F(NgRpcServerHandlerWsTest, HandleRequest_Successful_WsRequest) backend_->setRange(0, 1); runSpawn([&](boost::asio::yield_context yield) { Request::HttpHeaders const headers; - std::string const requestStr = R"json({"method":"some_method", "id": 1234, "api_version": 1})json"; + std::string const requestStr = R"JSON({"method":"some_method", "id": 1234, "api_version": 1})JSON"; auto const request = Request(requestStr, headers); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true)); @@ -578,7 +578,7 @@ TEST_F(NgRpcServerHandlerWsTest, HandleRequest_Successful_WsRequest_HasError) backend_->setRange(0, 1); runSpawn([&](boost::asio::yield_context yield) { Request::HttpHeaders const headers; - std::string const requestStr = R"json({"method":"some_method", "id": 1234, "api_version": 1})json"; + std::string const requestStr = R"JSON({"method":"some_method", "id": 1234, "api_version": 1})JSON"; auto const request = Request(requestStr, headers); EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true)); diff --git a/tests/unit/web/ng/ServerTests.cpp b/tests/unit/web/ng/ServerTests.cpp index b1dab0711..535c9b337 100644 --- a/tests/unit/web/ng/ServerTests.cpp +++ b/tests/unit/web/ng/ServerTests.cpp @@ -103,48 +103,48 @@ INSTANTIATE_TEST_CASE_P( testing::Values( MakeServerTestBundle{ "BadEndpoint", - R"json( + R"JSON( { "server": {"ip": "wrong", "port": 12345} } - )json", + )JSON", false }, MakeServerTestBundle{ "BadSslConfig", - R"json( + R"JSON( { "server": {"ip": "127.0.0.1", "port": 12345}, "ssl_cert_file": "some_file" } - )json", + )JSON", false }, MakeServerTestBundle{ "BadProcessingPolicy", - R"json( + R"JSON( { "server": {"ip": "127.0.0.1", "port": 12345, "processing_policy": "wrong"} } - )json", + )JSON", false }, MakeServerTestBundle{ "CorrectConfig_ParallelPolicy", - R"json( + R"JSON( { "server": {"ip": "127.0.0.1", "port": 12345, "processing_policy": "parallel"} } - )json", + )JSON", true }, MakeServerTestBundle{ "CorrectConfig_SequentPolicy", - R"json( + R"JSON( { "server": {"ip": "127.0.0.1", "port": 12345, "processing_policy": "sequent"} } - )json", + )JSON", true } ), @@ -358,7 +358,7 @@ TEST_F(ServerHttpTest, OnConnectCheckFailed) auto const response = client.receive(yield, std::chrono::milliseconds{100}); [&]() { ASSERT_TRUE(response.has_value()) << response.error().message(); }(); EXPECT_EQ(response->result(), http::status::too_many_requests); - EXPECT_EQ(response->body(), R"json({"error":"some error"})json"); + EXPECT_EQ(response->body(), R"JSON({"error":"some error"})JSON"); EXPECT_EQ(response->version(), 11); client.gracefulShutdown(); diff --git a/tests/unit/web/ng/impl/ErrorHandlingTests.cpp b/tests/unit/web/ng/impl/ErrorHandlingTests.cpp index 7bfdb86f7..57ddb96e0 100644 --- a/tests/unit/web/ng/impl/ErrorHandlingTests.cpp +++ b/tests/unit/web/ng/impl/ErrorHandlingTests.cpp @@ -92,7 +92,7 @@ INSTANTIATE_TEST_CASE_P( "WsRequest", false, rpc::Status{rpc::RippledError::rpcTOO_BUSY}, - R"({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})", + R"JSON({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})JSON", boost::beast::http::status::ok }, NgErrorHandlingMakeErrorTestBundle{ @@ -134,7 +134,7 @@ INSTANTIATE_TEST_CASE_P( "HttpRequest_RippledError", true, rpc::Status{rpc::RippledError::rpcTOO_BUSY}, - R"({"result":{"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"}})", + R"JSON({"result":{"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"}})JSON", boost::beast::http::status::bad_request }, }), @@ -198,7 +198,7 @@ INSTANTIATE_TEST_CASE_P( NgErrorHandlingMakeInternalErrorTestBundle{ "Request_WebsocketConnection", false, - std::string{R"({"id": 1, "api_version": 2})"}, + std::string{R"JSON({"id": 1, "api_version": 2})JSON"}, {{"error", "internal"}, {"error_code", 73}, {"error_message", "Internal error."}, @@ -211,7 +211,7 @@ INSTANTIATE_TEST_CASE_P( NgErrorHandlingMakeInternalErrorTestBundle{ "Request_WebsocketConnection_NoId", false, - std::string{R"({"api_version": 2})"}, + std::string{R"JSON({"api_version": 2})JSON"}, {{"error", "internal"}, {"error_code", 73}, {"error_message", "Internal error."}, @@ -223,7 +223,7 @@ INSTANTIATE_TEST_CASE_P( NgErrorHandlingMakeInternalErrorTestBundle{ "Request_HttpConnection", true, - std::string{R"({"id": 1, "api_version": 2})"}, + std::string{R"JSON({"id": 1, "api_version": 2})JSON"}, {{"result", {{"error", "internal"}, {"error_code", 73}, @@ -244,7 +244,7 @@ TEST_F(NgErrorHandlingTests, MakeNotReadyError) EXPECT_EQ( response.message(), std::string{ - R"({"result":{"error":"notReady","error_code":13,"error_message":"Not ready to handle this request.","status":"error","type":"response"}})" + R"JSON({"result":{"error":"notReady","error_code":13,"error_message":"Not ready to handle this request.","status":"error","type":"response"}})JSON" } ); auto const httpResponse = std::move(response).intoHttpResponse(); @@ -259,7 +259,7 @@ TEST_F(NgErrorHandlingTests, MakeTooBusyError_WebsocketRequest) EXPECT_EQ( response.message(), std::string{ - R"({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})" + R"JSON({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})JSON" } ); } @@ -271,7 +271,7 @@ TEST_F(NgErrorHandlingTests, sendTooBusyError_HttpConnection) EXPECT_EQ( response.message(), std::string{ - R"({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})" + R"JSON({"error":"tooBusy","error_code":9,"error_message":"The server is too busy to help you now.","status":"error","type":"response"})JSON" } ); auto const httpResponse = std::move(response).intoHttpResponse(); @@ -286,7 +286,7 @@ TEST_F(NgErrorHandlingTests, makeJsonParsingError_WebsocketConnection) EXPECT_EQ( response.message(), std::string{ - R"({"error":"badSyntax","error_code":1,"error_message":"Syntax error.","status":"error","type":"response"})" + R"JSON({"error":"badSyntax","error_code":1,"error_message":"Syntax error.","status":"error","type":"response"})JSON" } ); } @@ -327,31 +327,31 @@ INSTANTIATE_TEST_CASE_P( "NoRequest_WebsocketConnection", false, std::nullopt, - R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})" + R"JSON({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})JSON" }, NgErrorHandlingComposeErrorTestBundle{ "NoRequest_HttpConnection", true, std::nullopt, - R"({"result":{"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"}})" + R"JSON({"result":{"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"}})JSON" }, NgErrorHandlingComposeErrorTestBundle{ "Request_WebsocketConnection", false, boost::json::object{{"id", 1}, {"api_version", 2}}, - R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","id":1,"api_version":2,"request":{"id":1,"api_version":2}})", + R"JSON({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","id":1,"api_version":2,"request":{"id":1,"api_version":2}})JSON", }, NgErrorHandlingComposeErrorTestBundle{ "Request_WebsocketConnection_NoId", false, boost::json::object{{"api_version", 2}}, - R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","api_version":2,"request":{"api_version":2}})", + R"JSON({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","api_version":2,"request":{"api_version":2}})JSON", }, NgErrorHandlingComposeErrorTestBundle{ "Request_HttpConnection", true, boost::json::object{{"id", 1}, {"api_version", 2}}, - R"({"result":{"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","id":1,"request":{"id":1,"api_version":2}}})" + R"JSON({"result":{"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response","id":1,"request":{"id":1,"api_version":2}}})JSON" }} ), tests::util::kNAME_GENERATOR From 0b7fd64a4c846f6dc613b429bd8370d84e0dfd35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 11:08:46 +0100 Subject: [PATCH 14/33] ci: [DEPENDABOT] bump docker/build-push-action from 6.17.0 to 6.18.0 in /.github/actions/build_docker_image (#2175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.17.0 to 6.18.0.

Release notes

Sourced from docker/build-push-action's releases.

v6.18.0

[!NOTE] Build summary is now supported with Docker Build Cloud.

Full Changelog: https://github.com/docker/build-push-action/compare/v6.17.0...v6.18.0

Commits
  • 2634353 Merge pull request #1381 from docker/dependabot/npm_and_yarn/docker/actions-t...
  • c0432d2 chore: update generated content
  • 0bb1f27 set builder driver and endpoint attributes for dbc summary support
  • 5f9dbf9 chore(deps): Bump @​docker/actions-toolkit from 0.61.0 to 0.62.1
  • 0788c44 Merge pull request #1375 from crazy-max/remove-gcr
  • aa179ca e2e: remove GCR
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/build-push-action&package-manager=github_actions&previous-version=6.17.0&new-version=6.18.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build_docker_image/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build_docker_image/action.yml b/.github/actions/build_docker_image/action.yml index b0155cc9b..27fc32be8 100644 --- a/.github/actions/build_docker_image/action.yml +++ b/.github/actions/build_docker_image/action.yml @@ -54,7 +54,7 @@ runs: tags: ${{ inputs.tags }} - name: Build and push - uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: ${{ inputs.directory }} platforms: ${{ inputs.platforms }} From c7600057bc287ad02c34e8f9e6f0c0692906047a Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 2 Jun 2025 11:42:25 +0100 Subject: [PATCH 15/33] ci: Install zip in Dockerfile (#2176) --- docker/ci/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile index 21a4b11f6..e62f534d1 100644 --- a/docker/ci/Dockerfile +++ b/docker/ci/Dockerfile @@ -29,7 +29,7 @@ RUN apt-get -qq update \ # Install packages RUN apt update -qq \ - && apt install -y --no-install-recommends --no-install-suggests python3 python3-pip git git-lfs make ninja-build flex bison jq graphviz \ + && apt install -y --no-install-recommends --no-install-suggests python3 python3-pip git git-lfs make ninja-build flex bison jq graphviz zip \ clang-tidy-${LLVM_TOOLS_VERSION} clang-tools-${LLVM_TOOLS_VERSION} \ && pip3 install -q --upgrade --no-cache-dir pip && pip3 install -q --no-cache-dir conan==1.62 gcovr cmake==3.31.6 pre-commit \ && apt-get clean && apt remove -y software-properties-common From 49b4af1a56c17587d41cb56b4e56673732a83036 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 2 Jun 2025 11:43:44 +0100 Subject: [PATCH 16/33] fix: Add style to the name of pre-commit autoupdate PR title (#2177) --- .github/workflows/pre-commit-autoupdate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml index a45602896..52911448b 100644 --- a/.github/workflows/pre-commit-autoupdate.yml +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -33,7 +33,7 @@ jobs: GH_TOKEN: ${{ github.token }} with: branch: update/pre-commit-hooks - title: Update pre-commit hooks - commit-message: "style: update pre-commit hooks" + title: "style: Update pre-commit hooks" + commit-message: "style: Update pre-commit hooks" body: Update versions of pre-commit hooks to latest version. reviewers: "godexsoft,kuznetsss,PeterChen13579,mathbunnyru" From 19257f8aa936a0f4baca715676ca08e31bd13b68 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 2 Jun 2025 11:48:37 +0100 Subject: [PATCH 17/33] style: Beautify installation lists in Dockerfile (#2172) Needed for: https://github.com/XRPLF/clio/pull/2168 --- docker/ci/Dockerfile | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile index e62f534d1..c2199e96d 100644 --- a/docker/ci/Dockerfile +++ b/docker/ci/Dockerfile @@ -29,9 +29,26 @@ RUN apt-get -qq update \ # Install packages RUN apt update -qq \ - && apt install -y --no-install-recommends --no-install-suggests python3 python3-pip git git-lfs make ninja-build flex bison jq graphviz zip \ - clang-tidy-${LLVM_TOOLS_VERSION} clang-tools-${LLVM_TOOLS_VERSION} \ - && pip3 install -q --upgrade --no-cache-dir pip && pip3 install -q --no-cache-dir conan==1.62 gcovr cmake==3.31.6 pre-commit \ + && apt install -y --no-install-recommends --no-install-suggests \ + bison \ + clang-tidy-${LLVM_TOOLS_VERSION} \ + clang-tools-${LLVM_TOOLS_VERSION} \ + flex \ + git \ + git-lfs \ + graphviz \ + jq \ + make \ + ninja-build \ + python3 \ + python3-pip \ + zip \ + && pip3 install -q --upgrade --no-cache-dir pip \ + && pip3 install -q --no-cache-dir \ + cmake==3.31.6 \ + conan==1.62 \ + gcovr \ + pre-commit \ && apt-get clean && apt remove -y software-properties-common # Install gcc-12 and make ldconfig aware of the new libstdc++ location (for gcc) From 550f0fae85247f9c3f08fea8f9f9fba974d13230 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 3 Jun 2025 13:34:25 +0100 Subject: [PATCH 18/33] refactor: Use std::expected instead of std::variant for errors (#2160) --- src/rpc/RPCEngine.hpp | 24 ++-- src/rpc/RPCHelpers.cpp | 134 ++++++++++--------- src/rpc/RPCHelpers.hpp | 19 ++- src/rpc/common/Types.hpp | 8 +- src/rpc/handlers/AMMInfo.cpp | 8 +- src/rpc/handlers/AccountChannels.cpp | 16 +-- src/rpc/handlers/AccountCurrencies.cpp | 8 +- src/rpc/handlers/AccountInfo.cpp | 8 +- src/rpc/handlers/AccountLines.cpp | 16 +-- src/rpc/handlers/AccountNFTs.cpp | 8 +- src/rpc/handlers/AccountObjects.cpp | 16 +-- src/rpc/handlers/AccountOffers.cpp | 16 +-- src/rpc/handlers/AccountTx.cpp | 8 +- src/rpc/handlers/BookChanges.cpp | 8 +- src/rpc/handlers/BookOffers.cpp | 14 +- src/rpc/handlers/DepositAuthorized.cpp | 8 +- src/rpc/handlers/Feature.cpp | 8 +- src/rpc/handlers/GatewayBalances.cpp | 12 +- src/rpc/handlers/GetAggregatePrice.cpp | 8 +- src/rpc/handlers/Ledger.cpp | 8 +- src/rpc/handlers/LedgerData.cpp | 8 +- src/rpc/handlers/LedgerEntry.cpp | 22 +-- src/rpc/handlers/LedgerEntry.hpp | 2 +- src/rpc/handlers/MPTHolders.cpp | 8 +- src/rpc/handlers/NFTHistory.cpp | 8 +- src/rpc/handlers/NFTInfo.cpp | 8 +- src/rpc/handlers/NFTOffersCommon.cpp | 12 +- src/rpc/handlers/NFTsByIssuer.cpp | 8 +- src/rpc/handlers/NoRippleCheck.cpp | 8 +- src/rpc/handlers/Subscribe.cpp | 7 +- src/rpc/handlers/TransactionEntry.cpp | 8 +- src/rpc/handlers/Unsubscribe.cpp | 7 +- src/web/RPCServerHandler.hpp | 6 +- src/web/ng/RPCServerHandler.hpp | 6 +- tests/unit/rpc/ForwardingProxyTests.cpp | 8 +- tests/unit/rpc/RPCEngineTests.cpp | 27 ++-- tests/unit/rpc/RPCHelpersTests.cpp | 45 +++---- tests/unit/rpc/handlers/BookOffersTests.cpp | 24 ++-- tests/unit/rpc/handlers/SubscribeTests.cpp | 24 ++-- tests/unit/rpc/handlers/UnsubscribeTests.cpp | 4 +- 40 files changed, 295 insertions(+), 310 deletions(-) diff --git a/src/rpc/RPCEngine.hpp b/src/rpc/RPCEngine.hpp index 119665b59..4a1874e41 100644 --- a/src/rpc/RPCEngine.hpp +++ b/src/rpc/RPCEngine.hpp @@ -163,19 +163,17 @@ public: [this, &ctx](boost::asio::yield_context ) -> std::expected { auto result = buildResponseImpl(ctx); - auto extracted = std::visit( - util::OverloadSet{ - [&result](Status status - ) -> std::expected { - return std::unexpected{util::ResponseExpirationCache::Error{ - .status = std::move(status), .warnings = std::move(result.warnings) - }}; - }, - [](boost::json::object obj - ) -> std::expected { return obj; } - }, - std::move(result.response) - ); + + auto const extracted = + [&result]() -> std::expected { + if (result.response.has_value()) + return std::move(result.response).value(); + else + return std::unexpected{util::ResponseExpirationCache::Error{ + .status = std::move(result.response).error(), .warnings = std::move(result.warnings) + }}; + }(); + if (extracted.has_value()) { return util::ResponseExpirationCache::EntryData{ .lastUpdated = std::chrono::steady_clock::now(), .response = std::move(extracted).value() diff --git a/src/rpc/RPCHelpers.cpp b/src/rpc/RPCHelpers.cpp index c11d58d41..11d11aca2 100644 --- a/src/rpc/RPCHelpers.cpp +++ b/src/rpc/RPCHelpers.cpp @@ -467,23 +467,23 @@ parseStringAsUInt(std::string const& value) return index; } -std::variant +std::expected ledgerHeaderFromRequest(std::shared_ptr const& backend, web::Context const& ctx) { auto hashValue = ctx.params.contains("ledger_hash") ? ctx.params.at("ledger_hash") : nullptr; if (!hashValue.is_null()) { if (!hashValue.is_string()) - return Status{RippledError::rpcINVALID_PARAMS, "ledgerHashNotString"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "ledgerHashNotString"}}; ripple::uint256 ledgerHash; if (!ledgerHash.parseHex(boost::json::value_to(hashValue))) - return Status{RippledError::rpcINVALID_PARAMS, "ledgerHashMalformed"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "ledgerHashMalformed"}}; auto lgrInfo = backend->fetchLedgerByHash(ledgerHash, ctx.yield); if (!lgrInfo || lgrInfo->seq > ctx.range.maxSequence) - return Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"}; + return std::unexpected{Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"}}; return *lgrInfo; } @@ -507,18 +507,18 @@ ledgerHeaderFromRequest(std::shared_ptr const& bac } if (!ledgerSequence) - return Status{RippledError::rpcINVALID_PARAMS, "ledgerIndexMalformed"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "ledgerIndexMalformed"}}; auto lgrInfo = backend->fetchLedgerBySequence(*ledgerSequence, ctx.yield); if (!lgrInfo || lgrInfo->seq > ctx.range.maxSequence) - return Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"}; + return std::unexpected{Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"}}; return *lgrInfo; } // extract ledgerHeaderFromRequest's parameter from context -std::variant +std::expected getLedgerHeaderFromHashOrSeq( BackendInterface const& backend, boost::asio::yield_context yield, @@ -528,7 +528,7 @@ getLedgerHeaderFromHashOrSeq( ) { std::optional lgrInfo; - auto err = Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"}; + auto const err = std::unexpected{Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"}}; if (ledgerHash) { // invoke uint256's constructor to parse the hex string , instead of // copying buffer @@ -589,7 +589,7 @@ getStartHint(ripple::SLE const& sle, ripple::AccountID const& accountID) // traverse account's nfts // return Status if error occurs // return [nextpage, count of nft already found] if success -std::variant +std::expected traverseNFTObjects( BackendInterface const& backend, std::uint32_t sequence, @@ -605,7 +605,7 @@ traverseNFTObjects( // check if nextPage is valid if (nextPage != beast::zero and firstNFTPage.key != (nextPage & ~ripple::nft::pageMask)) - return Status{RippledError::rpcINVALID_PARAMS, "Invalid marker."}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "Invalid marker."}}; // no marker, start from the last page ripple::uint256 const currentPage = nextPage == beast::zero ? lastNFTPage.key : nextPage; @@ -618,7 +618,7 @@ traverseNFTObjects( return AccountCursor{.index = beast::zero, .hint = 0}; } // marker is in the right range, but still invalid - return Status{RippledError::rpcINVALID_PARAMS, "Invalid marker."}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "Invalid marker."}}; } // the object exists and the key is in right range, must be nft page @@ -641,7 +641,7 @@ traverseNFTObjects( return AccountCursor{.index = beast::zero, .hint = 0}; } -std::variant +std::expected traverseOwnedNodes( BackendInterface const& backend, ripple::AccountID const& accountID, @@ -656,7 +656,7 @@ traverseOwnedNodes( auto const maybeCursor = parseAccountCursor(jsonCursor); if (!maybeCursor) - return Status{RippledError::rpcINVALID_PARAMS, "Malformed cursor."}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "Malformed cursor."}}; // the format is checked in RPC framework level auto [hexCursor, startHint] = *maybeCursor; @@ -670,10 +670,10 @@ traverseOwnedNodes( if (nftIncluded and (!jsonCursor or isNftMarkerNonZero)) { auto const cursorMaybe = traverseNFTObjects(backend, sequence, accountID, hexCursor, limit, yield, atOwnedNode); - if (auto const status = std::get_if(&cursorMaybe)) - return *status; + if (!cursorMaybe.has_value()) + return cursorMaybe; - auto const [nextNFTPage, nftsCount] = std::get(cursorMaybe); + auto const [nextNFTPage, nftsCount] = cursorMaybe.value(); // if limit reach , we return the next page and max as marker if (nftsCount >= limit) @@ -694,7 +694,7 @@ traverseOwnedNodes( ); } -std::variant +std::expected traverseOwnedNodes( BackendInterface const& backend, ripple::Keylet const& owner, @@ -728,7 +728,7 @@ traverseOwnedNodes( auto hintDir = backend.fetchLedgerObject(hintIndex.key, sequence, yield); if (!hintDir) - return Status(ripple::rpcINVALID_PARAMS, "Invalid marker."); + return std::unexpected{Status(ripple::rpcINVALID_PARAMS, "Invalid marker.")}; ripple::SerialIter hintDirIt{hintDir->data(), hintDir->size()}; ripple::SLE const hintDirSle{hintDirIt, hintIndex.key}; @@ -736,7 +736,7 @@ traverseOwnedNodes( if (auto const& indexes = hintDirSle.getFieldV256(ripple::sfIndexes); std::ranges::find(indexes, hexMarker) == std::end(indexes)) { // the index specified by marker is not in the page specified by marker - return Status(ripple::rpcINVALID_PARAMS, "Invalid marker."); + return std::unexpected{Status(ripple::rpcINVALID_PARAMS, "Invalid marker.")}; } currentIndex = hintIndex; @@ -745,7 +745,7 @@ traverseOwnedNodes( auto const ownerDir = backend.fetchLedgerObject(currentIndex.key, sequence, yield); if (!ownerDir) - return Status(ripple::rpcINVALID_PARAMS, "Owner directory not found."); + return std::unexpected{Status(ripple::rpcINVALID_PARAMS, "Owner directory not found.")}; ripple::SerialIter ownedDirIt{ownerDir->data(), ownerDir->size()}; ripple::SLE const ownedDirSle{ownedDirIt, currentIndex.key}; @@ -1309,120 +1309,122 @@ postProcessOrderBook( } // get book via currency type -std::variant +std::expected parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency gets, ripple::AccountID getIssuer) { if (isXRP(pays) && !isXRP(payIssuer)) { - return Status{ + return std::unexpected{Status{ RippledError::rpcSRC_ISR_MALFORMED, "Unneeded field 'taker_pays.issuer' for XRP currency specification." - }; + }}; } if (!isXRP(pays) && isXRP(payIssuer)) { - return Status{ - RippledError::rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer." + return std::unexpected{ + Status{RippledError::rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer."} }; } if (ripple::isXRP(gets) && !ripple::isXRP(getIssuer)) { - return Status{ + return std::unexpected{Status{ RippledError::rpcDST_ISR_MALFORMED, "Unneeded field 'taker_gets.issuer' for XRP currency specification." - }; + }}; } if (!ripple::isXRP(gets) && ripple::isXRP(getIssuer)) { - return Status{ - RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer." + return std::unexpected{ + Status{RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."} }; } if (pays == gets && payIssuer == getIssuer) - return Status{RippledError::rpcBAD_MARKET, "badMarket"}; + return std::unexpected{Status{RippledError::rpcBAD_MARKET, "badMarket"}}; return ripple::Book{{pays, payIssuer}, {gets, getIssuer}}; } -std::variant +std::expected parseBook(boost::json::object const& request) { if (!request.contains("taker_pays")) - return Status{RippledError::rpcINVALID_PARAMS, "Missing field 'taker_pays'"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "Missing field 'taker_pays'"}}; if (!request.contains("taker_gets")) - return Status{RippledError::rpcINVALID_PARAMS, "Missing field 'taker_gets'"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "Missing field 'taker_gets'"}}; if (!request.at("taker_pays").is_object()) - return Status{RippledError::rpcINVALID_PARAMS, "Field 'taker_pays' is not an object"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "Field 'taker_pays' is not an object"}}; if (!request.at("taker_gets").is_object()) - return Status{RippledError::rpcINVALID_PARAMS, "Field 'taker_gets' is not an object"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "Field 'taker_gets' is not an object"}}; auto takerPays = request.at("taker_pays").as_object(); if (!takerPays.contains("currency")) - return Status{RippledError::rpcSRC_CUR_MALFORMED}; + return std::unexpected{Status{RippledError::rpcSRC_CUR_MALFORMED}}; if (!takerPays.at("currency").is_string()) - return Status{RippledError::rpcSRC_CUR_MALFORMED}; + return std::unexpected{Status{RippledError::rpcSRC_CUR_MALFORMED}}; auto takerGets = request.at("taker_gets").as_object(); if (!takerGets.contains("currency")) - return Status{RippledError::rpcDST_AMT_MALFORMED}; + return std::unexpected{Status{RippledError::rpcDST_AMT_MALFORMED}}; if (!takerGets.at("currency").is_string()) { - return Status{ + return std::unexpected{Status{ RippledError::rpcDST_AMT_MALFORMED, - }; + }}; } ripple::Currency payCurrency; if (!ripple::to_currency(payCurrency, boost::json::value_to(takerPays.at("currency")))) - return Status{RippledError::rpcSRC_CUR_MALFORMED}; + return std::unexpected{Status{RippledError::rpcSRC_CUR_MALFORMED}}; ripple::Currency getCurrency; if (!ripple::to_currency(getCurrency, boost::json::value_to(takerGets["currency"]))) - return Status{RippledError::rpcDST_AMT_MALFORMED}; + return std::unexpected{Status{RippledError::rpcDST_AMT_MALFORMED}}; ripple::AccountID payIssuer; if (takerPays.contains("issuer")) { if (!takerPays.at("issuer").is_string()) - return Status{RippledError::rpcINVALID_PARAMS, "takerPaysIssuerNotString"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "takerPaysIssuerNotString"}}; if (!ripple::to_issuer(payIssuer, boost::json::value_to(takerPays.at("issuer")))) - return Status{RippledError::rpcSRC_ISR_MALFORMED}; + return std::unexpected{Status{RippledError::rpcSRC_ISR_MALFORMED}}; if (payIssuer == ripple::noAccount()) - return Status{RippledError::rpcSRC_ISR_MALFORMED}; + return std::unexpected{Status{RippledError::rpcSRC_ISR_MALFORMED}}; } else { payIssuer = ripple::xrpAccount(); } if (isXRP(payCurrency) && !isXRP(payIssuer)) { - return Status{ + return std::unexpected{Status{ RippledError::rpcSRC_ISR_MALFORMED, "Unneeded field 'taker_pays.issuer' for XRP currency specification." - }; + }}; } if (!isXRP(payCurrency) && isXRP(payIssuer)) { - return Status{ - RippledError::rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer." + return std::unexpected{ + Status{RippledError::rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer."} }; } if ((!isXRP(payCurrency)) && (!takerPays.contains("issuer"))) - return Status{RippledError::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."}; + return std::unexpected{Status{RippledError::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."}}; ripple::AccountID getIssuer; if (takerGets.contains("issuer")) { if (!takerGets["issuer"].is_string()) - return Status{RippledError::rpcINVALID_PARAMS, "taker_gets.issuer should be string"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "taker_gets.issuer should be string"}}; if (!ripple::to_issuer(getIssuer, boost::json::value_to(takerGets.at("issuer")))) - return Status{RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."}; + return std::unexpected{ + Status{RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."} + }; if (getIssuer == ripple::noAccount()) { - return Status{ - RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer account one." + return std::unexpected{ + Status{RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer account one."} }; } } else { @@ -1430,34 +1432,34 @@ parseBook(boost::json::object const& request) } if (ripple::isXRP(getCurrency) && !ripple::isXRP(getIssuer)) { - return Status{ + return std::unexpected{Status{ RippledError::rpcDST_ISR_MALFORMED, "Unneeded field 'taker_gets.issuer' for XRP currency specification." - }; + }}; } if (!ripple::isXRP(getCurrency) && ripple::isXRP(getIssuer)) { - return Status{ - RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer." + return std::unexpected{ + Status{RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."} }; } if (payCurrency == getCurrency && payIssuer == getIssuer) - return Status{RippledError::rpcBAD_MARKET, "badMarket"}; + return std::unexpected{Status{RippledError::rpcBAD_MARKET, "badMarket"}}; return ripple::Book{{payCurrency, payIssuer}, {getCurrency, getIssuer}}; } -std::variant +std::expected parseTaker(boost::json::value const& taker) { std::optional takerID = {}; if (!taker.is_string()) - return {Status{RippledError::rpcINVALID_PARAMS, "takerNotString"}}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "takerNotString"}}; takerID = accountFromStringStrict(boost::json::value_to(taker)); if (!takerID) - return Status{RippledError::rpcBAD_ISSUER, "invalidTakerAccount"}; + return std::unexpected{Status{RippledError::rpcBAD_ISSUER, "invalidTakerAccount"}}; return *takerID; } @@ -1511,18 +1513,18 @@ isAdminCmd(std::string const& method, boost::json::object const& request) return false; } -std::variant +std::expected getNFTID(boost::json::object const& request) { if (!request.contains(JS(nft_id))) - return Status{RippledError::rpcINVALID_PARAMS, "missingTokenID"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "missingTokenID"}}; if (!request.at(JS(nft_id)).is_string()) - return Status{RippledError::rpcINVALID_PARAMS, "tokenIDNotString"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "tokenIDNotString"}}; ripple::uint256 tokenid; if (!tokenid.parseHex(boost::json::value_to(request.at(JS(nft_id))))) - return Status{RippledError::rpcINVALID_PARAMS, "malformedTokenID"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "malformedTokenID"}}; return tokenid; } diff --git a/src/rpc/RPCHelpers.hpp b/src/rpc/RPCHelpers.hpp index a2c830517..fcfeb4f6c 100644 --- a/src/rpc/RPCHelpers.hpp +++ b/src/rpc/RPCHelpers.hpp @@ -73,7 +73,6 @@ #include #include #include -#include #include namespace rpc { @@ -281,7 +280,7 @@ generatePubLedgerMessage( * @param ctx The context of the request * @return The ledger info or an error status */ -std::variant +std::expected ledgerHeaderFromRequest(std::shared_ptr const& backend, web::Context const& ctx); /** @@ -294,7 +293,7 @@ ledgerHeaderFromRequest(std::shared_ptr const& bac * @param maxSeq The maximum sequence to search * @return The ledger info or an error status */ -std::variant +std::expected getLedgerHeaderFromHashOrSeq( BackendInterface const& backend, boost::asio::yield_context yield, @@ -316,7 +315,7 @@ getLedgerHeaderFromHashOrSeq( * @param atOwnedNode The function to call for each owned node * @return The status or the account cursor */ -std::variant +std::expected traverseOwnedNodes( BackendInterface const& backend, ripple::Keylet const& owner, @@ -343,7 +342,7 @@ traverseOwnedNodes( * @param nftIncluded Whether to include NFTs * @return The status or the account cursor */ -std::variant +std::expected traverseOwnedNodes( BackendInterface const& backend, ripple::AccountID const& accountID, @@ -628,7 +627,7 @@ postProcessOrderBook( * @param getIssuer The issuer of the currency to get * @return The book or an error status */ -std::variant +std::expected parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency gets, ripple::AccountID getIssuer); /** @@ -637,7 +636,7 @@ parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency g * @param request The request * @return The book or an error status */ -std::variant +std::expected parseBook(boost::json::object const& request); /** @@ -646,7 +645,7 @@ parseBook(boost::json::object const& request); * @param taker The taker as json * @return The taker account or an error status */ -std::variant +std::expected parseTaker(boost::json::value const& taker); /** @@ -683,7 +682,7 @@ isAdminCmd(std::string const& method, boost::json::object const& request); * @param request The request * @return The NFTID or an error status */ -std::variant +std::expected getNFTID(boost::json::object const& request); /** @@ -793,7 +792,7 @@ parseRippleLibSeed(boost::json::value const& value); * @param atOwnedNode The function to call for each owned node * @return The account cursor or an error status */ -std::variant +std::expected traverseNFTObjects( BackendInterface const& backend, std::uint32_t sequence, diff --git a/src/rpc/common/Types.hpp b/src/rpc/common/Types.hpp index 6b68daddb..85beda00b 100644 --- a/src/rpc/common/Types.hpp +++ b/src/rpc/common/Types.hpp @@ -100,7 +100,7 @@ struct ReturnType { */ operator bool() const { - return result.operator bool(); + return result.has_value(); } std::expected result; @@ -137,7 +137,7 @@ struct Result { if (returnType) { response = std::move(returnType.result).value().as_object(); } else { - response = std::move(returnType.result).error(); + response = std::unexpected{std::move(returnType.result).error()}; } warnings = std::move(returnType.warnings); } @@ -147,7 +147,7 @@ struct Result { * * @param status The status to construct the result from */ - explicit Result(Status status) : response{std::move(status)} + explicit Result(Status status) : response{std::unexpected{std::move(status)}} { } @@ -160,7 +160,7 @@ struct Result { { } - std::variant response; + std::expected response; boost::json::array warnings; }; diff --git a/src/rpc/handlers/AMMInfo.cpp b/src/rpc/handlers/AMMInfo.cpp index e73c76b22..23b517df2 100644 --- a/src/rpc/handlers/AMMInfo.cpp +++ b/src/rpc/handlers/AMMInfo.cpp @@ -97,14 +97,14 @@ AMMInfoHandler::process(AMMInfoHandler::Input input, Context const& ctx) const auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "AMMInfo's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); if (input.accountID) { auto keylet = keylet::account(*input.accountID); diff --git a/src/rpc/handlers/AccountChannels.cpp b/src/rpc/handlers/AccountChannels.cpp index b17cb9593..6f2ef6ee0 100644 --- a/src/rpc/handlers/AccountChannels.cpp +++ b/src/rpc/handlers/AccountChannels.cpp @@ -86,14 +86,14 @@ AccountChannelsHandler::process(AccountChannelsHandler::Input input, Context con { auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "AccountChannel's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountID = accountFromStringStrict(input.account); auto const accountLedgerObject = sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield); @@ -114,19 +114,19 @@ AccountChannelsHandler::process(AccountChannelsHandler::Input input, Context con return true; }; - auto const next = traverseOwnedNodes( + auto const expectedNext = traverseOwnedNodes( *sharedPtrBackend_, *accountID, lgrInfo.seq, input.limit, input.marker, ctx.yield, addToResponse ); - if (auto status = std::get_if(&next)) - return Error{*status}; + if (!expectedNext.has_value()) + return Error{expectedNext.error()}; response.account = input.account; response.limit = input.limit; response.ledgerHash = ripple::strHex(lgrInfo.hash); response.ledgerIndex = lgrInfo.seq; - auto const nextMarker = std::get(next); + auto const nextMarker = expectedNext.value(); if (nextMarker.isNonZero()) response.marker = nextMarker.toString(); diff --git a/src/rpc/handlers/AccountCurrencies.cpp b/src/rpc/handlers/AccountCurrencies.cpp index 0fdf64b6a..471a61feb 100644 --- a/src/rpc/handlers/AccountCurrencies.cpp +++ b/src/rpc/handlers/AccountCurrencies.cpp @@ -48,14 +48,14 @@ AccountCurrenciesHandler::process(AccountCurrenciesHandler::Input input, Context { auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "AccountCurrencies' ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountID = accountFromStringStrict(input.account); auto const accountLedgerObject = diff --git a/src/rpc/handlers/AccountInfo.cpp b/src/rpc/handlers/AccountInfo.cpp index b9ee7c364..f90c7b15b 100644 --- a/src/rpc/handlers/AccountInfo.cpp +++ b/src/rpc/handlers/AccountInfo.cpp @@ -60,14 +60,14 @@ AccountInfoHandler::process(AccountInfoHandler::Input input, Context const& ctx) auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "AccountInfo's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountStr = input.account.value_or(input.ident.value_or("")); auto const accountID = accountFromStringStrict(accountStr); auto const accountKeylet = ripple::keylet::account(*accountID); diff --git a/src/rpc/handlers/AccountLines.cpp b/src/rpc/handlers/AccountLines.cpp index 6459e27d6..cd3d0d4c3 100644 --- a/src/rpc/handlers/AccountLines.cpp +++ b/src/rpc/handlers/AccountLines.cpp @@ -135,14 +135,14 @@ AccountLinesHandler::process(AccountLinesHandler::Input input, Context const& ct { auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "AccountLines' ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountID = accountFromStringStrict(input.account); auto const accountLedgerObject = sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield); @@ -171,14 +171,14 @@ AccountLinesHandler::process(AccountLinesHandler::Input input, Context const& ct } }; - auto const next = traverseOwnedNodes( + auto const expectedNext = traverseOwnedNodes( *sharedPtrBackend_, *accountID, lgrInfo.seq, input.limit, input.marker, ctx.yield, addToResponse ); - if (auto status = std::get_if(&next)) - return Error{*status}; + if (!expectedNext.has_value()) + return Error{expectedNext.error()}; - auto const nextMarker = std::get(next); + auto const nextMarker = expectedNext.value(); response.account = input.account; response.limit = input.limit; // not documented, diff --git a/src/rpc/handlers/AccountNFTs.cpp b/src/rpc/handlers/AccountNFTs.cpp index d59989cca..a9fd45418 100644 --- a/src/rpc/handlers/AccountNFTs.cpp +++ b/src/rpc/handlers/AccountNFTs.cpp @@ -54,14 +54,14 @@ AccountNFTsHandler::process(AccountNFTsHandler::Input input, Context const& ctx) { auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "AccountNFT's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountID = accountFromStringStrict(input.account); auto const accountLedgerObject = sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield); diff --git a/src/rpc/handlers/AccountObjects.cpp b/src/rpc/handlers/AccountObjects.cpp index cc720e7d1..9a8658b47 100644 --- a/src/rpc/handlers/AccountObjects.cpp +++ b/src/rpc/handlers/AccountObjects.cpp @@ -54,14 +54,14 @@ AccountObjectsHandler::process(AccountObjectsHandler::Input input, Context const { auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "AccountObject's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountID = accountFromStringStrict(input.account); auto const accountLedgerObject = sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield); @@ -97,19 +97,19 @@ AccountObjectsHandler::process(AccountObjectsHandler::Input input, Context const return true; }; - auto const next = traverseOwnedNodes( + auto const expectedNext = traverseOwnedNodes( *sharedPtrBackend_, *accountID, lgrInfo.seq, input.limit, input.marker, ctx.yield, addToResponse, true ); - if (auto status = std::get_if(&next)) - return Error{*status}; + if (!expectedNext.has_value()) + return Error{expectedNext.error()}; response.ledgerHash = ripple::strHex(lgrInfo.hash); response.ledgerIndex = lgrInfo.seq; response.limit = input.limit; response.account = input.account; - auto const& nextMarker = std::get(next); + auto const& nextMarker = expectedNext.value(); if (nextMarker.isNonZero()) response.marker = nextMarker.toString(); diff --git a/src/rpc/handlers/AccountOffers.cpp b/src/rpc/handlers/AccountOffers.cpp index 5e38dd14d..0483ed9cd 100644 --- a/src/rpc/handlers/AccountOffers.cpp +++ b/src/rpc/handlers/AccountOffers.cpp @@ -70,14 +70,14 @@ AccountOffersHandler::process(AccountOffersHandler::Input input, Context const& { auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "AccountOffer's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountID = accountFromStringStrict(input.account); auto const accountLedgerObject = sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield); @@ -97,14 +97,14 @@ AccountOffersHandler::process(AccountOffersHandler::Input input, Context const& return true; }; - auto const next = traverseOwnedNodes( + auto const expectedNext = traverseOwnedNodes( *sharedPtrBackend_, *accountID, lgrInfo.seq, input.limit, input.marker, ctx.yield, addToResponse ); - if (auto const status = std::get_if(&next)) - return Error{*status}; + if (!expectedNext.has_value()) + return Error{expectedNext.error()}; - auto const nextMarker = std::get(next); + auto const nextMarker = expectedNext.value(); if (nextMarker.isNonZero()) response.marker = nextMarker.toString(); diff --git a/src/rpc/handlers/AccountTx.cpp b/src/rpc/handlers/AccountTx.cpp index 882d5a983..ca919f33b 100644 --- a/src/rpc/handlers/AccountTx.cpp +++ b/src/rpc/handlers/AccountTx.cpp @@ -94,14 +94,14 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con if (!input.ledgerIndexMax && !input.ledgerIndexMin) { // mimic rippled, when both range and index specified, respect the range. // take ledger from ledgerHash or ledgerIndex only when range is not specified - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - maxIndex = minIndex = std::get(lgrInfoOrStatus).seq; + maxIndex = minIndex = expectedLgrInfo.value().seq; } } diff --git a/src/rpc/handlers/BookChanges.cpp b/src/rpc/handlers/BookChanges.cpp index 61cdd59bb..5365a49c3 100644 --- a/src/rpc/handlers/BookChanges.cpp +++ b/src/rpc/handlers/BookChanges.cpp @@ -48,14 +48,14 @@ BookChangesHandler::process(BookChangesHandler::Input input, Context const& ctx) auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "BookChanges' ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const transactions = sharedPtrBackend_->fetchAllTransactionsInLedger(lgrInfo.seq, ctx.yield); Output response; diff --git a/src/rpc/handlers/BookOffers.cpp b/src/rpc/handlers/BookOffers.cpp index 3c7420512..84e7eb21b 100644 --- a/src/rpc/handlers/BookOffers.cpp +++ b/src/rpc/handlers/BookOffers.cpp @@ -47,22 +47,22 @@ BookOffersHandler::Result BookOffersHandler::process(Input input, Context const& ctx) const { auto bookMaybe = parseBook(input.paysCurrency, input.paysID, input.getsCurrency, input.getsID); - if (auto const status = std::get_if(&bookMaybe)) - return Error{*status}; + if (!bookMaybe.has_value()) + return Error{bookMaybe.error()}; // check ledger auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "BookOffer's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); - auto const book = std::get(bookMaybe); + auto const& lgrInfo = expectedLgrInfo.value(); + auto const book = bookMaybe.value(); auto const bookKey = getBookBase(book); // TODO: Add performance metrics if needed in future diff --git a/src/rpc/handlers/DepositAuthorized.cpp b/src/rpc/handlers/DepositAuthorized.cpp index 38f181ceb..1f02ec327 100644 --- a/src/rpc/handlers/DepositAuthorized.cpp +++ b/src/rpc/handlers/DepositAuthorized.cpp @@ -55,14 +55,14 @@ DepositAuthorizedHandler::process(DepositAuthorizedHandler::Input input, Context auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "DepositAuthorized ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const sourceAccountID = accountFromStringStrict(input.sourceAccount); auto const destinationAccountID = accountFromStringStrict(input.destinationAccount); diff --git a/src/rpc/handlers/Feature.cpp b/src/rpc/handlers/Feature.cpp index 6a3d2f323..540daa502 100644 --- a/src/rpc/handlers/Feature.cpp +++ b/src/rpc/handlers/Feature.cpp @@ -59,14 +59,14 @@ FeatureHandler::process(FeatureHandler::Input input, Context const& ctx) const auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "Feature's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const& all = amendmentCenter_->getAll(); auto searchPredicate = [search = input.feature](auto const& feature) { diff --git a/src/rpc/handlers/GatewayBalances.cpp b/src/rpc/handlers/GatewayBalances.cpp index 5209f1fa7..8b99eca3c 100644 --- a/src/rpc/handlers/GatewayBalances.cpp +++ b/src/rpc/handlers/GatewayBalances.cpp @@ -62,15 +62,15 @@ GatewayBalancesHandler::process(GatewayBalancesHandler::Input input, Context con auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "GatewayBalances' ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; // check account - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountID = accountFromStringStrict(input.account); auto const accountLedgerObject = sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield); @@ -142,8 +142,8 @@ GatewayBalancesHandler::process(GatewayBalancesHandler::Input input, Context con addToResponse ); - if (auto status = std::get_if(&ret)) - return Error{*status}; + if (!ret.has_value()) + return Error{ret.error()}; output.accountID = input.account; output.ledgerHash = ripple::strHex(lgrInfo.hash); diff --git a/src/rpc/handlers/GetAggregatePrice.cpp b/src/rpc/handlers/GetAggregatePrice.cpp index fcf2a8f58..3563ae4a4 100644 --- a/src/rpc/handlers/GetAggregatePrice.cpp +++ b/src/rpc/handlers/GetAggregatePrice.cpp @@ -64,14 +64,14 @@ GetAggregatePriceHandler::process(GetAggregatePriceHandler::Input input, Context auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "GetAggregatePrice's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); // sorted descending by lastUpdateTime, ascending by AssetPrice using TimestampPricesBiMap = boost::bimaps::bimap< diff --git a/src/rpc/handlers/Ledger.cpp b/src/rpc/handlers/Ledger.cpp index 519b983a0..4f70d8a80 100644 --- a/src/rpc/handlers/Ledger.cpp +++ b/src/rpc/handlers/Ledger.cpp @@ -55,14 +55,14 @@ LedgerHandler::process(LedgerHandler::Input input, Context const& ctx) const auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "LedgerHandler's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); Output output; output.header = toJson(lgrInfo, input.binary, ctx.apiVersion); diff --git a/src/rpc/handlers/LedgerData.cpp b/src/rpc/handlers/LedgerData.cpp index 0b6d396e4..3a7be7918 100644 --- a/src/rpc/handlers/LedgerData.cpp +++ b/src/rpc/handlers/LedgerData.cpp @@ -64,14 +64,14 @@ LedgerDataHandler::process(Input input, Context const& ctx) const auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "LedgerData's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); Output output; diff --git a/src/rpc/handlers/LedgerEntry.cpp b/src/rpc/handlers/LedgerEntry.cpp index ebd04e99d..d312b9815 100644 --- a/src/rpc/handlers/LedgerEntry.cpp +++ b/src/rpc/handlers/LedgerEntry.cpp @@ -70,11 +70,11 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx) } else if (input.did) { key = ripple::keylet::did(*util::parseBase58Wrapper(*(input.did))).key; } else if (input.directory) { - auto const keyOrStatus = composeKeyFromDirectory(*input.directory); - if (auto const status = std::get_if(&keyOrStatus)) - return Error{*status}; + auto const expectedkey = composeKeyFromDirectory(*input.directory); + if (!expectedkey.has_value()) + return Error{expectedkey.error()}; - key = std::get(keyOrStatus); + key = expectedkey.value(); } else if (input.offer) { auto const id = util::parseBase58Wrapper(boost::json::value_to(input.offer->at(JS(account))) @@ -202,14 +202,14 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx) // check ledger exists auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "LedgerEntry's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto output = LedgerEntryHandler::Output{}; auto ledgerObject = sharedPtrBackend_->fetchLedgerObject(key, lgrInfo.seq, ctx.yield); @@ -243,16 +243,16 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx) return output; } -std::variant +std::expected LedgerEntryHandler::composeKeyFromDirectory(boost::json::object const& directory) noexcept { // can not specify both dir_root and owner. if (directory.contains(JS(dir_root)) && directory.contains(JS(owner))) - return Status{RippledError::rpcINVALID_PARAMS, "mayNotSpecifyBothDirRootAndOwner"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "mayNotSpecifyBothDirRootAndOwner"}}; // at least one should available if (!(directory.contains(JS(dir_root)) || directory.contains(JS(owner)))) - return Status{RippledError::rpcINVALID_PARAMS, "missingOwnerOrDirRoot"}; + return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "missingOwnerOrDirRoot"}}; uint64_t const subIndex = directory.contains(JS(sub_index)) ? boost::json::value_to(directory.at(JS(sub_index))) : 0; diff --git a/src/rpc/handlers/LedgerEntry.hpp b/src/rpc/handlers/LedgerEntry.hpp index 54b5a5adb..a7f261869 100644 --- a/src/rpc/handlers/LedgerEntry.hpp +++ b/src/rpc/handlers/LedgerEntry.hpp @@ -430,7 +430,7 @@ public: private: // dir_root and owner can not be both empty or filled at the same time // This function will return an error if this is the case - static std::variant + static std::expected composeKeyFromDirectory(boost::json::object const& directory) noexcept; /** diff --git a/src/rpc/handlers/MPTHolders.cpp b/src/rpc/handlers/MPTHolders.cpp index 0182b7a5e..94d8f547a 100644 --- a/src/rpc/handlers/MPTHolders.cpp +++ b/src/rpc/handlers/MPTHolders.cpp @@ -52,13 +52,13 @@ MPTHoldersHandler::process(MPTHoldersHandler::Input input, Context const& ctx) c auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "MPTHolder's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const limit = input.limit.value_or(MPTHoldersHandler::kLIMIT_DEFAULT); auto const mptID = ripple::uint192{input.mptID.c_str()}; diff --git a/src/rpc/handlers/NFTHistory.cpp b/src/rpc/handlers/NFTHistory.cpp index 91dbfbf31..22385869b 100644 --- a/src/rpc/handlers/NFTHistory.cpp +++ b/src/rpc/handlers/NFTHistory.cpp @@ -80,14 +80,14 @@ NFTHistoryHandler::process(NFTHistoryHandler::Input input, Context const& ctx) c if (input.ledgerIndexMax || input.ledgerIndexMin) return Error{Status{RippledError::rpcINVALID_PARAMS, "containsLedgerSpecifierAndRange"}}; - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - maxIndex = minIndex = std::get(lgrInfoOrStatus).seq; + maxIndex = minIndex = expectedLgrInfo.value().seq; } std::optional cursor; diff --git a/src/rpc/handlers/NFTInfo.cpp b/src/rpc/handlers/NFTInfo.cpp index 9fadcd16b..7dd20c7d7 100644 --- a/src/rpc/handlers/NFTInfo.cpp +++ b/src/rpc/handlers/NFTInfo.cpp @@ -50,14 +50,14 @@ NFTInfoHandler::process(NFTInfoHandler::Input input, Context const& ctx) const auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "NFTInfo's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const maybeNft = sharedPtrBackend_->fetchNFT(tokenID, lgrInfo.seq, ctx.yield); if (not maybeNft.has_value()) diff --git a/src/rpc/handlers/NFTOffersCommon.cpp b/src/rpc/handlers/NFTOffersCommon.cpp index e0ff6e8bb..1dfba3c2b 100644 --- a/src/rpc/handlers/NFTOffersCommon.cpp +++ b/src/rpc/handlers/NFTOffersCommon.cpp @@ -93,14 +93,14 @@ NFTOffersHandlerBase::iterateOfferDirectory( auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "NFTOffersCommon's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); // TODO: just check for existence without pulling if (not sharedPtrBackend_->fetchLedgerObject(directory.key, lgrInfo.seq, yield)) @@ -156,8 +156,8 @@ NFTOffersHandlerBase::iterateOfferDirectory( } ); - if (auto status = std::get_if(&result)) - return Error{*status}; + if (!result.has_value()) + return Error{result.error()}; if (offers.size() == reserve) { output.limit = input.limit; diff --git a/src/rpc/handlers/NFTsByIssuer.cpp b/src/rpc/handlers/NFTsByIssuer.cpp index 54b777d8c..0e27c519f 100644 --- a/src/rpc/handlers/NFTsByIssuer.cpp +++ b/src/rpc/handlers/NFTsByIssuer.cpp @@ -51,13 +51,13 @@ NFTsByIssuerHandler::process(NFTsByIssuerHandler::Input input, Context const& ct auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "NFTsByIssuer's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto const status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const limit = input.limit.value_or(NFTsByIssuerHandler::kLIMIT_DEFAULT); diff --git a/src/rpc/handlers/NoRippleCheck.cpp b/src/rpc/handlers/NoRippleCheck.cpp index 8eec13db3..5bc841417 100644 --- a/src/rpc/handlers/NoRippleCheck.cpp +++ b/src/rpc/handlers/NoRippleCheck.cpp @@ -61,14 +61,14 @@ NoRippleCheckHandler::process(NoRippleCheckHandler::Input input, Context const& auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "NoRippleCheck's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; - auto const lgrInfo = std::get(lgrInfoOrStatus); + auto const& lgrInfo = expectedLgrInfo.value(); auto const accountID = accountFromStringStrict(input.account); auto const keylet = ripple::keylet::account(*accountID).key; auto const accountObj = sharedPtrBackend_->fetchLedgerObject(keylet, lgrInfo.seq, ctx.yield); diff --git a/src/rpc/handlers/Subscribe.cpp b/src/rpc/handlers/Subscribe.cpp index 6a302aca4..53e4e8342 100644 --- a/src/rpc/handlers/Subscribe.cpp +++ b/src/rpc/handlers/Subscribe.cpp @@ -92,8 +92,8 @@ SubscribeHandler::spec([[maybe_unused]] uint32_t apiVersion) } auto const parsedBook = parseBook(book.as_object()); - if (auto const status = std::get_if(&parsedBook)) - return Error(*status); + if (!parsedBook) + return Error(parsedBook.error()); } return MaybeError{}; @@ -298,7 +298,8 @@ tag_invoke(boost::json::value_to_tag, boost::json::valu internalBook.snapshot = snapshot->value().as_bool(); auto const parsedBookMaybe = parseBook(book.as_object()); - internalBook.book = std::get(parsedBookMaybe); + ASSERT(parsedBookMaybe.has_value(), "Book parsing failed"); + internalBook.book = parsedBookMaybe.value(); input.books->push_back(internalBook); } } diff --git a/src/rpc/handlers/TransactionEntry.cpp b/src/rpc/handlers/TransactionEntry.cpp index 83a983bbb..7c26f68b5 100644 --- a/src/rpc/handlers/TransactionEntry.cpp +++ b/src/rpc/handlers/TransactionEntry.cpp @@ -46,17 +46,17 @@ TransactionEntryHandler::process(TransactionEntryHandler::Input input, Context c auto const range = sharedPtrBackend_->fetchLedgerRange(); ASSERT(range.has_value(), "TransactionEntry's ledger range must be available"); - auto const lgrInfoOrStatus = getLedgerHeaderFromHashOrSeq( + auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq( *sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence ); - if (auto status = std::get_if(&lgrInfoOrStatus)) - return Error{*status}; + if (!expectedLgrInfo.has_value()) + return Error{expectedLgrInfo.error()}; auto output = TransactionEntryHandler::Output{}; output.apiVersion = ctx.apiVersion; - output.ledgerHeader = std::get(lgrInfoOrStatus); + output.ledgerHeader = expectedLgrInfo.value(); auto const dbRet = sharedPtrBackend_->fetchTransaction(ripple::uint256{input.txHash.c_str()}, ctx.yield); // Note: transaction_entry is meant to only search a specified ledger for // the specified transaction. tx searches the entire range of history. For diff --git a/src/rpc/handlers/Unsubscribe.cpp b/src/rpc/handlers/Unsubscribe.cpp index 64def3cdf..ff6e245ec 100644 --- a/src/rpc/handlers/Unsubscribe.cpp +++ b/src/rpc/handlers/Unsubscribe.cpp @@ -66,8 +66,8 @@ UnsubscribeHandler::spec([[maybe_unused]] uint32_t apiVersion) return Error{Status{RippledError::rpcINVALID_PARAMS, "bothNotBool"}}; auto const parsedBook = parseBook(book.as_object()); - if (auto const status = std::get_if(&parsedBook)) - return Error(*status); + if (!parsedBook.has_value()) + return Error(parsedBook.error()); } return MaybeError{}; @@ -193,7 +193,8 @@ tag_invoke(boost::json::value_to_tag, boost::json::va internalBook.both = both->value().as_bool(); auto const parsedBookMaybe = parseBook(book.as_object()); - internalBook.book = std::get(parsedBookMaybe); + ASSERT(parsedBookMaybe.has_value(), "Invalid book format"); + internalBook.book = parsedBookMaybe.value(); input.books->push_back(internalBook); } } diff --git a/src/web/RPCServerHandler.hpp b/src/web/RPCServerHandler.hpp index 38494fae5..851f62a7f 100644 --- a/src/web/RPCServerHandler.hpp +++ b/src/web/RPCServerHandler.hpp @@ -216,9 +216,9 @@ private: boost::json::object response; - if (auto const status = std::get_if(&result.response)) { + if (!result.response.has_value()) { // note: error statuses are counted/notified in buildResponse itself - response = web::impl::ErrorHelper(connection, request).composeError(*status); + response = web::impl::ErrorHelper(connection, request).composeError(result.response.error()); auto const responseStr = boost::json::serialize(response); LOG(perfLog_.debug()) << context->tag() << "Encountered error: " << responseStr; @@ -227,7 +227,7 @@ private: // This can still technically be an error. Clio counts forwarded requests as successful. rpcEngine_->notifyComplete(context->method, us); - auto& json = std::get(result.response); + auto& json = result.response.value(); auto const isForwarded = json.contains("forwarded") && json.at("forwarded").is_bool() && json.at("forwarded").as_bool(); diff --git a/src/web/ng/RPCServerHandler.hpp b/src/web/ng/RPCServerHandler.hpp index 64db00627..628c0bde2 100644 --- a/src/web/ng/RPCServerHandler.hpp +++ b/src/web/ng/RPCServerHandler.hpp @@ -264,9 +264,9 @@ private: boost::json::object response; - if (auto const status = std::get_if(&result.response)) { + if (!result.response.has_value()) { // note: error statuses are counted/notified in buildResponse itself - response = impl::ErrorHelper(rawRequest, request).composeError(*status); + response = impl::ErrorHelper(rawRequest, request).composeError(result.response.error()); auto const responseStr = boost::json::serialize(response); LOG(perfLog_.debug()) << context->tag() << "Encountered error: " << responseStr; @@ -275,7 +275,7 @@ private: // This can still technically be an error. Clio counts forwarded requests as successful. rpcEngine_->notifyComplete(context->method, us); - auto& json = std::get(result.response); + auto& json = result.response.value(); auto const isForwarded = json.contains("forwarded") && json.at("forwarded").is_bool() && json.at("forwarded").as_bool(); diff --git a/tests/unit/rpc/ForwardingProxyTests.cpp b/tests/unit/rpc/ForwardingProxyTests.cpp index e1b41bcb6..fe152d304 100644 --- a/tests/unit/rpc/ForwardingProxyTests.cpp +++ b/tests/unit/rpc/ForwardingProxyTests.cpp @@ -318,8 +318,7 @@ TEST_F(RPCForwardingProxyTest, ForwardCallsBalancerWithCorrectParams) auto const res = proxy_.forward(ctx); - auto const data = std::get_if(&res.response); - EXPECT_TRUE(data != nullptr); + EXPECT_TRUE(res.response.has_value()); }); } @@ -348,8 +347,7 @@ TEST_F(RPCForwardingProxyTest, ForwardingFailYieldsErrorStatus) auto const res = proxy_.forward(ctx); - auto const status = std::get_if(&res.response); - EXPECT_TRUE(status != nullptr); - EXPECT_EQ(*status, rpc::ClioError::EtlInvalidResponse); + EXPECT_FALSE(res.response.has_value()); + EXPECT_EQ(res.response.error(), rpc::ClioError::EtlInvalidResponse); }); } diff --git a/tests/unit/rpc/RPCEngineTests.cpp b/tests/unit/rpc/RPCEngineTests.cpp index 4b17b9d7c..bdd5734a3 100644 --- a/tests/unit/rpc/RPCEngineTests.cpp +++ b/tests/unit/rpc/RPCEngineTests.cpp @@ -258,13 +258,11 @@ TEST_P(RPCEngineFlowParameterTest, Test) ); auto const res = engine->buildResponse(ctx); - auto const status = std::get_if(&res.response); - auto const response = std::get_if(&res.response); - ASSERT_EQ(status == nullptr, testBundle.response.has_value()); + ASSERT_EQ(res.response.has_value(), testBundle.response.has_value()); if (testBundle.response.has_value()) { - EXPECT_EQ(*response, testBundle.response.value()); + EXPECT_EQ(res.response.value(), testBundle.response.value()); } else { - EXPECT_EQ(*status, testBundle.status.value()); + EXPECT_EQ(res.response.error(), testBundle.status.value()); } }); } @@ -295,9 +293,8 @@ TEST_F(RPCEngineTest, ThrowDatabaseError) ); auto const res = engine->buildResponse(ctx); - auto const status = std::get_if(&res.response); - ASSERT_TRUE(status != nullptr); - EXPECT_EQ(*status, Status{RippledError::rpcTOO_BUSY}); + ASSERT_FALSE(res.response.has_value()); + EXPECT_EQ(res.response.error(), Status{RippledError::rpcTOO_BUSY}); }); } @@ -327,9 +324,8 @@ TEST_F(RPCEngineTest, ThrowException) ); auto const res = engine->buildResponse(ctx); - auto const status = std::get_if(&res.response); - ASSERT_TRUE(status != nullptr); - EXPECT_EQ(*status, Status{RippledError::rpcINTERNAL}); + ASSERT_FALSE(res.response.has_value()); + EXPECT_EQ(res.response.error(), Status{RippledError::rpcINTERNAL}); }); } @@ -454,8 +450,8 @@ TEST_P(RPCEngineCacheParameterTest, Test) ); auto const res = engine->buildResponse(ctx); - auto const response = std::get_if(&res.response); - EXPECT_EQ(*response, boost::json::parse(R"JSON({ "computed": "world_50"})JSON").as_object()); + ASSERT_TRUE(res.response.has_value()); + EXPECT_EQ(res.response.value(), boost::json::parse(R"JSON({ "computed": "world_50"})JSON").as_object()); }); } } @@ -499,9 +495,8 @@ TEST_F(RPCEngineTest, NotCacheIfErrorHappen) ); auto const res = engine->buildResponse(ctx); - auto const error = std::get_if(&res.response); - ASSERT_NE(error, nullptr); - EXPECT_EQ(*error, rpc::Status{"Very custom error"}); + ASSERT_FALSE(res.response.has_value()); + EXPECT_EQ(res.response.error(), rpc::Status{"Very custom error"}); }); } } diff --git a/tests/unit/rpc/RPCHelpersTests.cpp b/tests/unit/rpc/RPCHelpersTests.cpp index bd45a0a3b..cf45cbc52 100644 --- a/tests/unit/rpc/RPCHelpersTests.cpp +++ b/tests/unit/rpc/RPCHelpersTests.cpp @@ -103,10 +103,9 @@ TEST_F(RPCHelpersTest, TraverseOwnedNodesMarkerInvalidIndexNotHex) auto ret = traverseOwnedNodes(*backend_, account, 9, 10, "nothex,10", yield, [](auto) { }); - auto status = std::get_if(&ret); - EXPECT_TRUE(status != nullptr); - EXPECT_EQ(*status, ripple::rpcINVALID_PARAMS); - EXPECT_EQ(status->message, "Malformed cursor."); + EXPECT_FALSE(ret.has_value()); + EXPECT_EQ(ret.error(), ripple::rpcINVALID_PARAMS); + EXPECT_EQ(ret.error().message, "Malformed cursor."); }); ctx_.run(); } @@ -118,10 +117,9 @@ TEST_F(RPCHelpersTest, TraverseOwnedNodesMarkerInvalidPageNotInt) auto ret = traverseOwnedNodes(*backend_, account, 9, 10, "nothex,abc", yield, [](auto) { }); - auto status = std::get_if(&ret); - EXPECT_TRUE(status != nullptr); - EXPECT_EQ(*status, ripple::rpcINVALID_PARAMS); - EXPECT_EQ(status->message, "Malformed cursor."); + EXPECT_FALSE(ret.has_value()); + EXPECT_EQ(ret.error(), ripple::rpcINVALID_PARAMS); + EXPECT_EQ(ret.error().message, "Malformed cursor."); }); ctx_.run(); } @@ -148,13 +146,10 @@ TEST_F(RPCHelpersTest, TraverseOwnedNodesNoInputMarker) EXPECT_CALL(*backend_, doFetchLedgerObjects).Times(1); boost::asio::spawn(ctx_, [this, &account](boost::asio::yield_context yield) { - auto ret = traverseOwnedNodes(*backend_, account, 9, 10, {}, yield, [](auto) { - - }); - auto cursor = std::get_if(&ret); - EXPECT_TRUE(cursor != nullptr); + auto ret = traverseOwnedNodes(*backend_, account, 9, 10, {}, yield, [](auto) {}); + EXPECT_TRUE(ret.has_value()); EXPECT_EQ( - cursor->toString(), + ret.value().toString(), "0000000000000000000000000000000000000000000000000000000000000000," "0" ); @@ -192,10 +187,9 @@ TEST_F(RPCHelpersTest, TraverseOwnedNodesNoInputMarkerReturnSamePageMarker) boost::asio::spawn(ctx_, [this, &account](boost::asio::yield_context yield) { auto count = 0; auto ret = traverseOwnedNodes(*backend_, account, 9, 10, {}, yield, [&](auto) { count++; }); - auto cursor = std::get_if(&ret); - EXPECT_TRUE(cursor != nullptr); + EXPECT_TRUE(ret.has_value()); EXPECT_EQ(count, 10); - EXPECT_EQ(cursor->toString(), fmt::format("{},0", kINDEX1)); + EXPECT_EQ(ret.value().toString(), fmt::format("{},0", kINDEX1)); }); ctx_.run(); } @@ -244,10 +238,9 @@ TEST_F(RPCHelpersTest, TraverseOwnedNodesNoInputMarkerReturnOtherPageMarker) boost::asio::spawn(ctx_, [&, this](boost::asio::yield_context yield) { auto count = 0; auto ret = traverseOwnedNodes(*backend_, account, 9, kLIMIT, {}, yield, [&](auto) { count++; }); - auto cursor = std::get_if(&ret); - EXPECT_TRUE(cursor != nullptr); + EXPECT_TRUE(ret.has_value()); EXPECT_EQ(count, kLIMIT); - EXPECT_EQ(cursor->toString(), fmt::format("{},{}", kINDEX1, kNEXT_PAGE)); + EXPECT_EQ(ret.value().toString(), fmt::format("{},{}", kINDEX1, kNEXT_PAGE)); }); ctx_.run(); } @@ -291,10 +284,9 @@ TEST_F(RPCHelpersTest, TraverseOwnedNodesWithMarkerReturnSamePageMarker) auto ret = traverseOwnedNodes( *backend_, account, 9, kLIMIT, fmt::format("{},{}", kINDEX1, kPAGE_NUM), yield, [&](auto) { count++; } ); - auto cursor = std::get_if(&ret); - EXPECT_TRUE(cursor != nullptr); + EXPECT_TRUE(ret.has_value()); EXPECT_EQ(count, kLIMIT); - EXPECT_EQ(cursor->toString(), fmt::format("{},{}", kINDEX1, kPAGE_NUM)); + EXPECT_EQ(ret.value().toString(), fmt::format("{},{}", kINDEX1, kPAGE_NUM)); }); ctx_.run(); } @@ -328,10 +320,9 @@ TEST_F(RPCHelpersTest, TraverseOwnedNodesWithUnexistingIndexMarker) auto ret = traverseOwnedNodes( *backend_, account, 9, kLIMIT, fmt::format("{},{}", kINDEX2, kPAGE_NUM), yield, [&](auto) { count++; } ); - auto status = std::get_if(&ret); - EXPECT_TRUE(status != nullptr); - EXPECT_EQ(*status, ripple::rpcINVALID_PARAMS); - EXPECT_EQ(status->message, "Invalid marker."); + EXPECT_FALSE(ret.has_value()); + EXPECT_EQ(ret.error(), ripple::rpcINVALID_PARAMS); + EXPECT_EQ(ret.error().message, "Invalid marker."); }); ctx_.run(); } diff --git a/tests/unit/rpc/handlers/BookOffersTests.cpp b/tests/unit/rpc/handlers/BookOffersTests.cpp index a00ec0516..1a5e1b2b7 100644 --- a/tests/unit/rpc/handlers/BookOffersTests.cpp +++ b/tests/unit/rpc/handlers/BookOffersTests.cpp @@ -611,12 +611,12 @@ generateNormalPathBookOffersTestBundles() kPAYS20_USD_GETS10_XRP_BOOK_DIR ); - auto const getsXRPPaysUSDBook = getBookBase(std::get( - rpc::parseBook(ripple::to_currency("USD"), account, ripple::xrpCurrency(), ripple::xrpAccount()) - )); - auto const getsUSDPaysXRPBook = getBookBase(std::get( - rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), account) - )); + auto const getsXRPPaysUSDBook = getBookBase( + rpc::parseBook(ripple::to_currency("USD"), account, ripple::xrpCurrency(), ripple::xrpAccount()).value() + ); + auto const getsUSDPaysXRPBook = getBookBase( + rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), account).value() + ); auto const getsXRPPaysUSDInputJson = fmt::format( R"JSON({{ @@ -1450,9 +1450,9 @@ TEST_F(RPCBookOffersHandlerTest, Limit) // return valid book dir EXPECT_CALL(*backend_, doFetchSuccessorKey).Times(1); - auto const getsXRPPaysUSDBook = getBookBase(std::get( - rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()) - )); + auto const getsXRPPaysUSDBook = getBookBase( + rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value() + ); ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, seq, _)) .WillByDefault(Return(ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR})); @@ -1523,9 +1523,9 @@ TEST_F(RPCBookOffersHandlerTest, LimitMoreThanMax) // return valid book dir EXPECT_CALL(*backend_, doFetchSuccessorKey).Times(1); - auto const getsXRPPaysUSDBook = getBookBase(std::get( - rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()) - )); + auto const getsXRPPaysUSDBook = getBookBase( + rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value() + ); ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, seq, _)) .WillByDefault(Return(ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR})); diff --git a/tests/unit/rpc/handlers/SubscribeTests.cpp b/tests/unit/rpc/handlers/SubscribeTests.cpp index b0b0ec816..0bd95464f 100644 --- a/tests/unit/rpc/handlers/SubscribeTests.cpp +++ b/tests/unit/rpc/handlers/SubscribeTests.cpp @@ -822,13 +822,13 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet) auto const issuer = getAccountIdWithString(kACCOUNT); - auto const getsXRPPaysUSDBook = getBookBase(std::get( - rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()) - )); + auto const getsXRPPaysUSDBook = getBookBase( + rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value() + ); - auto const reversedBook = getBookBase(std::get( - rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer) - )); + auto const reversedBook = getBookBase( + rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer).value() + ); ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, kMAX_SEQ, _)) .WillByDefault(Return(ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR})); @@ -992,13 +992,13 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet) auto const issuer = getAccountIdWithString(kACCOUNT); - auto const getsXRPPaysUSDBook = getBookBase(std::get( - rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()) - )); + auto const getsXRPPaysUSDBook = getBookBase( + rpc::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()).value() + ); - auto const reversedBook = getBookBase(std::get( - rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer) - )); + auto const reversedBook = getBookBase( + rpc::parseBook(ripple::xrpCurrency(), ripple::xrpAccount(), ripple::to_currency("USD"), issuer).value() + ); ON_CALL(*backend_, doFetchSuccessorKey(getsXRPPaysUSDBook, kMAX_SEQ, _)) .WillByDefault(Return(ripple::uint256{kPAYS20_USD_GETS10_XRP_BOOK_DIR})); diff --git a/tests/unit/rpc/handlers/UnsubscribeTests.cpp b/tests/unit/rpc/handlers/UnsubscribeTests.cpp index f46f4021f..94e0f7440 100644 --- a/tests/unit/rpc/handlers/UnsubscribeTests.cpp +++ b/tests/unit/rpc/handlers/UnsubscribeTests.cpp @@ -653,7 +653,7 @@ TEST_F(RPCUnsubscribeTest, Books) )); auto const parsedBookMaybe = rpc::parseBook(input.as_object().at("books").as_array()[0].as_object()); - auto const book = std::get(parsedBookMaybe); + auto const book = parsedBookMaybe.value(); EXPECT_CALL(*mockSubscriptionManagerPtr_, unsubBook(book, _)).Times(1); EXPECT_CALL(*mockSubscriptionManagerPtr_, unsubBook(ripple::reversed(book), _)).Times(1); @@ -686,7 +686,7 @@ TEST_F(RPCUnsubscribeTest, SingleBooks) )); auto const parsedBookMaybe = rpc::parseBook(input.as_object().at("books").as_array()[0].as_object()); - auto const book = std::get(parsedBookMaybe); + auto const book = parsedBookMaybe.value(); EXPECT_CALL(*mockSubscriptionManagerPtr_, unsubBook(book, _)).Times(1); From 357b96ab0da391fdb9c844622d4e19d620c7d23f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 18:15:08 +0100 Subject: [PATCH 19/33] style: clang-tidy auto fixes (#2184) Fixes #2183. Please review and commit clang-tidy fixes. Co-authored-by: mathbunnyru <12270691+mathbunnyru@users.noreply.github.com> --- src/rpc/RPCEngine.hpp | 10 +++++----- src/rpc/RPCHelpers.cpp | 4 ++-- src/rpc/handlers/AMMInfo.cpp | 1 - src/rpc/handlers/AccountChannels.cpp | 1 - src/rpc/handlers/AccountCurrencies.cpp | 1 - src/rpc/handlers/AccountInfo.cpp | 1 - src/rpc/handlers/AccountLines.cpp | 1 - src/rpc/handlers/AccountNFTs.cpp | 1 - src/rpc/handlers/AccountObjects.cpp | 1 - src/rpc/handlers/AccountOffers.cpp | 1 - src/rpc/handlers/AccountTx.cpp | 1 - src/rpc/handlers/BookChanges.cpp | 2 -- src/rpc/handlers/BookOffers.cpp | 2 -- src/rpc/handlers/DepositAuthorized.cpp | 1 - src/rpc/handlers/Feature.cpp | 1 - src/rpc/handlers/GatewayBalances.cpp | 1 - src/rpc/handlers/GetAggregatePrice.cpp | 1 - src/rpc/handlers/Ledger.cpp | 2 -- src/rpc/handlers/LedgerData.cpp | 1 - src/rpc/handlers/LedgerEntry.cpp | 1 - src/rpc/handlers/MPTHolders.cpp | 1 - src/rpc/handlers/NFTHistory.cpp | 1 - src/rpc/handlers/NFTInfo.cpp | 1 - src/rpc/handlers/NFTOffersCommon.cpp | 1 - src/rpc/handlers/NFTsByIssuer.cpp | 1 - src/rpc/handlers/NoRippleCheck.cpp | 1 - src/rpc/handlers/Subscribe.cpp | 1 - src/rpc/handlers/TransactionEntry.cpp | 1 - src/rpc/handlers/Unsubscribe.cpp | 1 - tests/unit/rpc/ForwardingProxyTests.cpp | 1 - tests/unit/rpc/RPCEngineTests.cpp | 1 - tests/unit/rpc/RPCHelpersTests.cpp | 1 - tests/unit/rpc/handlers/BookOffersTests.cpp | 1 - tests/unit/rpc/handlers/SubscribeTests.cpp | 1 - 34 files changed, 7 insertions(+), 42 deletions(-) diff --git a/src/rpc/RPCEngine.hpp b/src/rpc/RPCEngine.hpp index 4a1874e41..0fe41b9c2 100644 --- a/src/rpc/RPCEngine.hpp +++ b/src/rpc/RPCEngine.hpp @@ -166,12 +166,12 @@ public: auto const extracted = [&result]() -> std::expected { - if (result.response.has_value()) + if (result.response.has_value()) { return std::move(result.response).value(); - else - return std::unexpected{util::ResponseExpirationCache::Error{ - .status = std::move(result.response).error(), .warnings = std::move(result.warnings) - }}; + } + return std::unexpected{util::ResponseExpirationCache::Error{ + .status = std::move(result.response).error(), .warnings = std::move(result.warnings) + }}; }(); if (extracted.has_value()) { diff --git a/src/rpc/RPCHelpers.cpp b/src/rpc/RPCHelpers.cpp index 11d11aca2..43e240965 100644 --- a/src/rpc/RPCHelpers.cpp +++ b/src/rpc/RPCHelpers.cpp @@ -99,7 +99,6 @@ #include #include #include -#include #include // local to compilation unit loggers @@ -1417,10 +1416,11 @@ parseBook(boost::json::object const& request) if (!takerGets["issuer"].is_string()) return std::unexpected{Status{RippledError::rpcINVALID_PARAMS, "taker_gets.issuer should be string"}}; - if (!ripple::to_issuer(getIssuer, boost::json::value_to(takerGets.at("issuer")))) + if (!ripple::to_issuer(getIssuer, boost::json::value_to(takerGets.at("issuer")))) { return std::unexpected{ Status{RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."} }; + } if (getIssuer == ripple::noAccount()) { return std::unexpected{ diff --git a/src/rpc/handlers/AMMInfo.cpp b/src/rpc/handlers/AMMInfo.cpp index 23b517df2..4aad6a1f7 100644 --- a/src/rpc/handlers/AMMInfo.cpp +++ b/src/rpc/handlers/AMMInfo.cpp @@ -57,7 +57,6 @@ #include #include #include -#include namespace { diff --git a/src/rpc/handlers/AccountChannels.cpp b/src/rpc/handlers/AccountChannels.cpp index 6f2ef6ee0..d9dfff662 100644 --- a/src/rpc/handlers/AccountChannels.cpp +++ b/src/rpc/handlers/AccountChannels.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/AccountCurrencies.cpp b/src/rpc/handlers/AccountCurrencies.cpp index 471a61feb..ba34aa391 100644 --- a/src/rpc/handlers/AccountCurrencies.cpp +++ b/src/rpc/handlers/AccountCurrencies.cpp @@ -40,7 +40,6 @@ #include #include #include -#include namespace rpc { AccountCurrenciesHandler::Result diff --git a/src/rpc/handlers/AccountInfo.cpp b/src/rpc/handlers/AccountInfo.cpp index f90c7b15b..4f03c7f4d 100644 --- a/src/rpc/handlers/AccountInfo.cpp +++ b/src/rpc/handlers/AccountInfo.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/AccountLines.cpp b/src/rpc/handlers/AccountLines.cpp index cd3d0d4c3..6021356e2 100644 --- a/src/rpc/handlers/AccountLines.cpp +++ b/src/rpc/handlers/AccountLines.cpp @@ -43,7 +43,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/AccountNFTs.cpp b/src/rpc/handlers/AccountNFTs.cpp index a9fd45418..a517a8c59 100644 --- a/src/rpc/handlers/AccountNFTs.cpp +++ b/src/rpc/handlers/AccountNFTs.cpp @@ -45,7 +45,6 @@ #include #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/AccountObjects.cpp b/src/rpc/handlers/AccountObjects.cpp index 9a8658b47..574137bf4 100644 --- a/src/rpc/handlers/AccountObjects.cpp +++ b/src/rpc/handlers/AccountObjects.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/AccountOffers.cpp b/src/rpc/handlers/AccountOffers.cpp index 0483ed9cd..c079f9e73 100644 --- a/src/rpc/handlers/AccountOffers.cpp +++ b/src/rpc/handlers/AccountOffers.cpp @@ -41,7 +41,6 @@ #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/AccountTx.cpp b/src/rpc/handlers/AccountTx.cpp index ca919f33b..cd8bb7248 100644 --- a/src/rpc/handlers/AccountTx.cpp +++ b/src/rpc/handlers/AccountTx.cpp @@ -46,7 +46,6 @@ #include #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/BookChanges.cpp b/src/rpc/handlers/BookChanges.cpp index 5365a49c3..5078579de 100644 --- a/src/rpc/handlers/BookChanges.cpp +++ b/src/rpc/handlers/BookChanges.cpp @@ -21,7 +21,6 @@ #include "data/Types.hpp" #include "rpc/BookChangesHelper.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -37,7 +36,6 @@ #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/BookOffers.cpp b/src/rpc/handlers/BookOffers.cpp index 84e7eb21b..0322ee6a4 100644 --- a/src/rpc/handlers/BookOffers.cpp +++ b/src/rpc/handlers/BookOffers.cpp @@ -19,7 +19,6 @@ #include "rpc/handlers/BookOffers.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -39,7 +38,6 @@ #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/DepositAuthorized.cpp b/src/rpc/handlers/DepositAuthorized.cpp index 1f02ec327..b3fcb0ff3 100644 --- a/src/rpc/handlers/DepositAuthorized.cpp +++ b/src/rpc/handlers/DepositAuthorized.cpp @@ -45,7 +45,6 @@ #include #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/Feature.cpp b/src/rpc/handlers/Feature.cpp index 540daa502..0f054be6b 100644 --- a/src/rpc/handlers/Feature.cpp +++ b/src/rpc/handlers/Feature.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/GatewayBalances.cpp b/src/rpc/handlers/GatewayBalances.cpp index 8b99eca3c..a312aedc1 100644 --- a/src/rpc/handlers/GatewayBalances.cpp +++ b/src/rpc/handlers/GatewayBalances.cpp @@ -50,7 +50,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/GetAggregatePrice.cpp b/src/rpc/handlers/GetAggregatePrice.cpp index 3563ae4a4..9a27a845e 100644 --- a/src/rpc/handlers/GetAggregatePrice.cpp +++ b/src/rpc/handlers/GetAggregatePrice.cpp @@ -54,7 +54,6 @@ #include #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/Ledger.cpp b/src/rpc/handlers/Ledger.cpp index 4f70d8a80..0aa99fe71 100644 --- a/src/rpc/handlers/Ledger.cpp +++ b/src/rpc/handlers/Ledger.cpp @@ -20,7 +20,6 @@ #include "rpc/handlers/Ledger.hpp" #include "data/Types.hpp" -#include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/Types.hpp" @@ -46,7 +45,6 @@ #include #include #include -#include namespace rpc { LedgerHandler::Result diff --git a/src/rpc/handlers/LedgerData.cpp b/src/rpc/handlers/LedgerData.cpp index 3a7be7918..788c8fd86 100644 --- a/src/rpc/handlers/LedgerData.cpp +++ b/src/rpc/handlers/LedgerData.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/LedgerEntry.cpp b/src/rpc/handlers/LedgerEntry.cpp index d312b9815..2b39dc3ab 100644 --- a/src/rpc/handlers/LedgerEntry.cpp +++ b/src/rpc/handlers/LedgerEntry.cpp @@ -54,7 +54,6 @@ #include #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/MPTHolders.cpp b/src/rpc/handlers/MPTHolders.cpp index 94d8f547a..22eda31c4 100644 --- a/src/rpc/handlers/MPTHolders.cpp +++ b/src/rpc/handlers/MPTHolders.cpp @@ -40,7 +40,6 @@ #include #include -#include using namespace ripple; diff --git a/src/rpc/handlers/NFTHistory.cpp b/src/rpc/handlers/NFTHistory.cpp index 22385869b..35f8be779 100644 --- a/src/rpc/handlers/NFTHistory.cpp +++ b/src/rpc/handlers/NFTHistory.cpp @@ -44,7 +44,6 @@ #include #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/NFTInfo.cpp b/src/rpc/handlers/NFTInfo.cpp index 7dd20c7d7..35950b11e 100644 --- a/src/rpc/handlers/NFTInfo.cpp +++ b/src/rpc/handlers/NFTInfo.cpp @@ -37,7 +37,6 @@ #include #include -#include using namespace ripple; diff --git a/src/rpc/handlers/NFTOffersCommon.cpp b/src/rpc/handlers/NFTOffersCommon.cpp index 1dfba3c2b..ddb0b3277 100644 --- a/src/rpc/handlers/NFTOffersCommon.cpp +++ b/src/rpc/handlers/NFTOffersCommon.cpp @@ -48,7 +48,6 @@ #include #include #include -#include #include using namespace ripple; diff --git a/src/rpc/handlers/NFTsByIssuer.cpp b/src/rpc/handlers/NFTsByIssuer.cpp index 0e27c519f..1d332b494 100644 --- a/src/rpc/handlers/NFTsByIssuer.cpp +++ b/src/rpc/handlers/NFTsByIssuer.cpp @@ -39,7 +39,6 @@ #include #include -#include using namespace ripple; diff --git a/src/rpc/handlers/NoRippleCheck.cpp b/src/rpc/handlers/NoRippleCheck.cpp index 5bc841417..31557f7f2 100644 --- a/src/rpc/handlers/NoRippleCheck.cpp +++ b/src/rpc/handlers/NoRippleCheck.cpp @@ -51,7 +51,6 @@ #include #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/Subscribe.cpp b/src/rpc/handlers/Subscribe.cpp index 53e4e8342..5e6c7482a 100644 --- a/src/rpc/handlers/Subscribe.cpp +++ b/src/rpc/handlers/Subscribe.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/src/rpc/handlers/TransactionEntry.cpp b/src/rpc/handlers/TransactionEntry.cpp index 7c26f68b5..2890f03ec 100644 --- a/src/rpc/handlers/TransactionEntry.cpp +++ b/src/rpc/handlers/TransactionEntry.cpp @@ -36,7 +36,6 @@ #include #include -#include namespace rpc { diff --git a/src/rpc/handlers/Unsubscribe.cpp b/src/rpc/handlers/Unsubscribe.cpp index ff6e245ec..4dc687660 100644 --- a/src/rpc/handlers/Unsubscribe.cpp +++ b/src/rpc/handlers/Unsubscribe.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include namespace rpc { diff --git a/tests/unit/rpc/ForwardingProxyTests.cpp b/tests/unit/rpc/ForwardingProxyTests.cpp index fe152d304..3e79bbfca 100644 --- a/tests/unit/rpc/ForwardingProxyTests.cpp +++ b/tests/unit/rpc/ForwardingProxyTests.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include using namespace rpc; diff --git a/tests/unit/rpc/RPCEngineTests.cpp b/tests/unit/rpc/RPCEngineTests.cpp index bdd5734a3..d924fedc6 100644 --- a/tests/unit/rpc/RPCEngineTests.cpp +++ b/tests/unit/rpc/RPCEngineTests.cpp @@ -53,7 +53,6 @@ #include #include #include -#include #include using namespace data; diff --git a/tests/unit/rpc/RPCHelpersTests.cpp b/tests/unit/rpc/RPCHelpersTests.cpp index cf45cbc52..b1837eaf9 100644 --- a/tests/unit/rpc/RPCHelpersTests.cpp +++ b/tests/unit/rpc/RPCHelpersTests.cpp @@ -62,7 +62,6 @@ #include #include #include -#include #include using namespace data; diff --git a/tests/unit/rpc/handlers/BookOffersTests.cpp b/tests/unit/rpc/handlers/BookOffersTests.cpp index 1a5e1b2b7..148812a2b 100644 --- a/tests/unit/rpc/handlers/BookOffersTests.cpp +++ b/tests/unit/rpc/handlers/BookOffersTests.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/unit/rpc/handlers/SubscribeTests.cpp b/tests/unit/rpc/handlers/SubscribeTests.cpp index 0bd95464f..4fe27e3eb 100644 --- a/tests/unit/rpc/handlers/SubscribeTests.cpp +++ b/tests/unit/rpc/handlers/SubscribeTests.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include From b3f3259b14ebd497563f584a4546481bc0898acf Mon Sep 17 00:00:00 2001 From: Sergey Kuznetsov Date: Wed, 4 Jun 2025 15:01:30 +0100 Subject: [PATCH 20/33] feat: Use new web server by default (#2182) Fixes #1781. --- src/app/CliArgs.cpp | 4 +- src/app/CliArgs.hpp | 1 - src/app/ClioApplication.cpp | 68 +- src/app/ClioApplication.hpp | 4 +- src/app/Stopper.hpp | 4 +- src/app/WebHandlers.cpp | 30 +- src/app/WebHandlers.hpp | 32 +- src/main/Main.cpp | 2 +- src/util/config/ConfigDefinition.hpp | 1 - src/web/CMakeLists.txt | 16 +- src/web/{ng => }/Connection.cpp | 6 +- src/web/{ng => }/Connection.hpp | 10 +- src/web/{ng => }/Error.hpp | 4 +- src/web/HttpSession.hpp | 144 -- src/web/{ng => }/MessageHandler.hpp | 10 +- src/web/PlainWsSession.hpp | 204 --- src/web/{ng => }/ProcessingPolicy.hpp | 4 +- src/web/README.md | 66 +- src/web/RPCServerHandler.hpp | 227 ++- src/web/{ng => }/Request.cpp | 6 +- src/web/{ng => }/Request.hpp | 4 +- src/web/{ng => }/Response.cpp | 10 +- src/web/{ng => }/Response.hpp | 6 +- src/web/{ng => }/Server.cpp | 20 +- src/web/Server.hpp | 454 ++--- src/web/SslHttpSession.hpp | 188 -- src/web/SslWsSession.hpp | 208 --- src/web/SubscriptionContext.cpp | 53 +- src/web/SubscriptionContext.hpp | 62 +- src/web/{ng => }/impl/Concepts.hpp | 4 +- src/web/{ng => }/impl/ConnectionHandler.cpp | 20 +- src/web/{ng => }/impl/ConnectionHandler.hpp | 16 +- src/web/{ng => }/impl/ErrorHandling.cpp | 10 +- src/web/impl/ErrorHandling.hpp | 193 +- src/web/impl/HttpBase.hpp | 327 ---- src/web/{ng => }/impl/HttpConnection.hpp | 16 +- src/web/{ng => }/impl/ServerSslContext.cpp | 6 +- src/web/{ng => }/impl/ServerSslContext.hpp | 4 +- src/web/impl/WsBase.hpp | 318 ---- src/web/{ng => }/impl/WsConnection.hpp | 14 +- src/web/ng/RPCServerHandler.hpp | 393 ---- src/web/ng/Server.hpp | 192 -- src/web/ng/SubscriptionContext.cpp | 100 -- src/web/ng/SubscriptionContext.hpp | 132 -- src/web/ng/impl/ErrorHandling.hpp | 114 -- tests/common/web/{ng => }/MockConnection.hpp | 24 +- .../web/{ng => }/impl/MockHttpConnection.hpp | 22 +- .../web/{ng => }/impl/MockWsConnection.hpp | 20 +- tests/unit/CMakeLists.txt | 19 +- tests/unit/app/CliArgsTests.cpp | 24 - tests/unit/app/StopperTests.cpp | 4 +- tests/unit/app/WebHandlersTests.cpp | 32 +- tests/unit/web/RPCServerHandlerTests.cpp | 1573 ++++++----------- tests/unit/web/{ng => }/RequestTests.cpp | 4 +- tests/unit/web/{ng => }/ResponseTests.cpp | 8 +- tests/unit/web/ServerTests.cpp | 1168 ++++++------ tests/unit/web/SubscriptionContextTests.cpp | 144 +- .../{ng => }/impl/ConnectionHandlerTests.cpp | 20 +- tests/unit/web/impl/ErrorHandlingTests.cpp | 477 ++--- .../web/{ng => }/impl/HttpConnectionTests.cpp | 10 +- .../{ng => }/impl/ServerSslContextTests.cpp | 4 +- .../web/{ng => }/impl/WsConnectionTests.cpp | 14 +- tests/unit/web/ng/RPCServerHandlerTests.cpp | 611 ------- tests/unit/web/ng/ServerTests.cpp | 583 ------ .../unit/web/ng/SubscriptionContextTests.cpp | 172 -- tests/unit/web/ng/impl/ErrorHandlingTests.cpp | 358 ---- 66 files changed, 2177 insertions(+), 6821 deletions(-) rename src/web/{ng => }/Connection.cpp (95%) rename src/web/{ng => }/Connection.hpp (97%) rename src/web/{ng => }/Error.hpp (96%) delete mode 100644 src/web/HttpSession.hpp rename src/web/{ng => }/MessageHandler.hpp (90%) delete mode 100644 src/web/PlainWsSession.hpp rename src/web/{ng => }/ProcessingPolicy.hpp (96%) rename src/web/{ng => }/Request.cpp (98%) rename src/web/{ng => }/Request.hpp (99%) rename src/web/{ng => }/Response.cpp (97%) rename src/web/{ng => }/Response.hpp (98%) rename src/web/{ng => }/Server.cpp (97%) delete mode 100644 src/web/SslHttpSession.hpp delete mode 100644 src/web/SslWsSession.hpp rename src/web/{ng => }/impl/Concepts.hpp (96%) rename src/web/{ng => }/impl/ConnectionHandler.cpp (97%) rename src/web/{ng => }/impl/ConnectionHandler.hpp (95%) rename src/web/{ng => }/impl/ErrorHandling.cpp (97%) delete mode 100644 src/web/impl/HttpBase.hpp rename src/web/{ng => }/impl/HttpConnection.hpp (96%) rename src/web/{ng => }/impl/ServerSslContext.cpp (97%) rename src/web/{ng => }/impl/ServerSslContext.hpp (96%) delete mode 100644 src/web/impl/WsBase.hpp rename src/web/{ng => }/impl/WsConnection.hpp (96%) delete mode 100644 src/web/ng/RPCServerHandler.hpp delete mode 100644 src/web/ng/Server.hpp delete mode 100644 src/web/ng/SubscriptionContext.cpp delete mode 100644 src/web/ng/SubscriptionContext.hpp delete mode 100644 src/web/ng/impl/ErrorHandling.hpp rename tests/common/web/{ng => }/MockConnection.hpp (76%) rename tests/common/web/{ng => }/impl/MockHttpConnection.hpp (82%) rename tests/common/web/{ng => }/impl/MockWsConnection.hpp (79%) rename tests/unit/web/{ng => }/RequestTests.cpp (99%) rename tests/unit/web/{ng => }/ResponseTests.cpp (98%) rename tests/unit/web/{ng => }/impl/ConnectionHandlerTests.cpp (98%) rename tests/unit/web/{ng => }/impl/HttpConnectionTests.cpp (98%) rename tests/unit/web/{ng => }/impl/ServerSslContextTests.cpp (98%) rename tests/unit/web/{ng => }/impl/WsConnectionTests.cpp (98%) delete mode 100644 tests/unit/web/ng/RPCServerHandlerTests.cpp delete mode 100644 tests/unit/web/ng/ServerTests.cpp delete mode 100644 tests/unit/web/ng/SubscriptionContextTests.cpp delete mode 100644 tests/unit/web/ng/impl/ErrorHandlingTests.cpp diff --git a/src/app/CliArgs.cpp b/src/app/CliArgs.cpp index 374ee9865..5aae7ed7a 100644 --- a/src/app/CliArgs.cpp +++ b/src/app/CliArgs.cpp @@ -47,7 +47,6 @@ CliArgs::parse(int argc, char const* argv[]) ("help,h", "Print help message and exit") ("version,v", "Print version and exit") ("conf,c", po::value()->default_value(kDEFAULT_CONFIG_PATH), "Configuration file") - ("ng-web-server,w", "Use ng-web-server") ("migrate", po::value(), "Start migration helper") ("verify", "Checks the validity of config values") ("config-description,d", po::value(), "Generate config description markdown file") @@ -93,8 +92,7 @@ CliArgs::parse(int argc, char const* argv[]) if (parsed.count("verify") != 0u) return Action{Action::VerifyConfig{.configPath = std::move(configPath)}}; - return Action{Action::Run{.configPath = std::move(configPath), .useNgWebServer = parsed.count("ng-web-server") != 0} - }; + return Action{Action::Run{.configPath = std::move(configPath)}}; } } // namespace app diff --git a/src/app/CliArgs.hpp b/src/app/CliArgs.hpp index 8f2a8c359..aa465b5e4 100644 --- a/src/app/CliArgs.hpp +++ b/src/app/CliArgs.hpp @@ -45,7 +45,6 @@ public: /** @brief Run action. */ struct Run { std::string configPath; ///< Configuration file path. - bool useNgWebServer; ///< Whether to use a ng web server }; /** @brief Exit action. */ diff --git a/src/app/ClioApplication.cpp b/src/app/ClioApplication.cpp index 34a754acf..36b5c62e5 100644 --- a/src/app/ClioApplication.cpp +++ b/src/app/ClioApplication.cpp @@ -49,8 +49,6 @@ #include "web/dosguard/IntervalSweepHandler.hpp" #include "web/dosguard/Weights.hpp" #include "web/dosguard/WhitelistHandler.hpp" -#include "web/ng/RPCServerHandler.hpp" -#include "web/ng/Server.hpp" #include @@ -96,7 +94,7 @@ ClioApplication::ClioApplication(util::config::ClioConfigDefinition const& confi } int -ClioApplication::run(bool const useNgWebServer) +ClioApplication::run() { auto const threads = config_.get("io_threads"); LOG(util::LogService::info()) << "Number of io threads = " << threads; @@ -170,51 +168,37 @@ ClioApplication::run(bool const useNgWebServer) auto const rpcEngine = RPCEngineType::makeRPCEngine(config_, backend, balancer, dosGuard, workQueue, counters, handlerProvider); - if (useNgWebServer or config_.get("server.__ng_web_server")) { - web::ng::RPCServerHandler handler{config_, backend, rpcEngine, etl, dosGuard}; + web::RPCServerHandler handler{config_, backend, rpcEngine, etl, dosGuard}; - auto expectedAdminVerifier = web::makeAdminVerificationStrategy(config_); - if (not expectedAdminVerifier.has_value()) { - LOG(util::LogService::error()) << "Error creating admin verifier: " << expectedAdminVerifier.error(); - return EXIT_FAILURE; - } - auto const adminVerifier = std::move(expectedAdminVerifier).value(); + auto expectedAdminVerifier = web::makeAdminVerificationStrategy(config_); + if (not expectedAdminVerifier.has_value()) { + LOG(util::LogService::error()) << "Error creating admin verifier: " << expectedAdminVerifier.error(); + return EXIT_FAILURE; + } + auto const adminVerifier = std::move(expectedAdminVerifier).value(); - auto httpServer = web::ng::makeServer(config_, OnConnectCheck{dosGuard}, DisconnectHook{dosGuard}, ioc); + auto httpServer = web::makeServer(config_, OnConnectCheck{dosGuard}, DisconnectHook{dosGuard}, ioc); - if (not httpServer.has_value()) { - LOG(util::LogService::error()) << "Error creating web server: " << httpServer.error(); - return EXIT_FAILURE; - } - - httpServer->onGet("/metrics", MetricsHandler{adminVerifier}); - httpServer->onGet("/health", HealthCheckHandler{}); - auto requestHandler = RequestHandler{adminVerifier, handler}; - httpServer->onPost("/", requestHandler); - httpServer->onWs(std::move(requestHandler)); - - auto const maybeError = httpServer->run(); - if (maybeError.has_value()) { - LOG(util::LogService::error()) << "Error starting web server: " << *maybeError; - return EXIT_FAILURE; - } - - appStopper_.setOnStop( - Stopper::makeOnStopCallback(httpServer.value(), *balancer, *etl, *subscriptions, *backend, ioc) - ); - - // Blocks until stopped. - // When stopped, shared_ptrs fall out of scope - // Calls destructors on all resources, and destructs in order - start(ioc, threads); - - return EXIT_SUCCESS; + if (not httpServer.has_value()) { + LOG(util::LogService::error()) << "Error creating web server: " << httpServer.error(); + return EXIT_FAILURE; } - // Init the web server - auto handler = std::make_shared>(config_, backend, rpcEngine, etl, dosGuard); + httpServer->onGet("/metrics", MetricsHandler{adminVerifier}); + httpServer->onGet("/health", HealthCheckHandler{}); + auto requestHandler = RequestHandler{adminVerifier, handler}; + httpServer->onPost("/", requestHandler); + httpServer->onWs(std::move(requestHandler)); - auto const httpServer = web::makeHttpServer(config_, ioc, dosGuard, handler); + auto const maybeError = httpServer->run(); + if (maybeError.has_value()) { + LOG(util::LogService::error()) << "Error starting web server: " << *maybeError; + return EXIT_FAILURE; + } + + appStopper_.setOnStop( + Stopper::makeOnStopCallback(httpServer.value(), *balancer, *etl, *subscriptions, *backend, ioc) + ); // Blocks until stopped. // When stopped, shared_ptrs fall out of scope diff --git a/src/app/ClioApplication.hpp b/src/app/ClioApplication.hpp index 871fcd6a5..9d03ed0d4 100644 --- a/src/app/ClioApplication.hpp +++ b/src/app/ClioApplication.hpp @@ -44,12 +44,10 @@ public: /** * @brief Run the application * - * @param useNgWebServer Whether to use the new web server - * * @return exit code */ int - run(bool useNgWebServer); + run(); }; } // namespace app diff --git a/src/app/Stopper.hpp b/src/app/Stopper.hpp index daba1164e..056f6f3cd 100644 --- a/src/app/Stopper.hpp +++ b/src/app/Stopper.hpp @@ -25,7 +25,7 @@ #include "feed/SubscriptionManagerInterface.hpp" #include "util/CoroutineGroup.hpp" #include "util/log/Logger.hpp" -#include "web/ng/Server.hpp" +#include "web/Server.hpp" #include #include @@ -74,7 +74,7 @@ public: * @param ioc The io_context to stop. * @return The callback to be called on application stop. */ - template + template static std::function makeOnStopCallback( ServerType& server, diff --git a/src/app/WebHandlers.cpp b/src/app/WebHandlers.cpp index 97cddb7a3..20c3069ff 100644 --- a/src/app/WebHandlers.cpp +++ b/src/app/WebHandlers.cpp @@ -22,11 +22,11 @@ #include "util/Assert.hpp" #include "util/prometheus/Http.hpp" #include "web/AdminVerificationStrategy.hpp" +#include "web/Connection.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" #include "web/SubscriptionContextInterface.hpp" #include "web/dosguard/DOSGuardInterface.hpp" -#include "web/ng/Connection.hpp" -#include "web/ng/Request.hpp" -#include "web/ng/Response.hpp" #include #include @@ -41,13 +41,13 @@ OnConnectCheck::OnConnectCheck(web::dosguard::DOSGuardInterface& dosguard) : dos { } -std::expected -OnConnectCheck::operator()(web::ng::Connection const& connection) +std::expected +OnConnectCheck::operator()(web::Connection const& connection) { dosguard_.get().increment(connection.ip()); if (not dosguard_.get().isOk(connection.ip())) { return std::unexpected{ - web::ng::Response{boost::beast::http::status::too_many_requests, "Too many requests", connection} + web::Response{boost::beast::http::status::too_many_requests, "Too many requests", connection} }; } @@ -59,7 +59,7 @@ DisconnectHook::DisconnectHook(web::dosguard::DOSGuardInterface& dosguard) : dos } void -DisconnectHook::operator()(web::ng::Connection const& connection) +DisconnectHook::operator()(web::Connection const& connection) { dosguard_.get().decrement(connection.ip()); } @@ -69,10 +69,10 @@ MetricsHandler::MetricsHandler(std::shared_ptr a { } -web::ng::Response +web::Response MetricsHandler::operator()( - web::ng::Request const& request, - web::ng::ConnectionMetadata& connectionMetadata, + web::Request const& request, + web::ConnectionMetadata& connectionMetadata, web::SubscriptionContextPtr, boost::asio::yield_context ) @@ -86,13 +86,13 @@ MetricsHandler::operator()( httpRequest, adminVerifier_->isAdmin(httpRequest, connectionMetadata.ip()) ); ASSERT(maybeResponse.has_value(), "Got unexpected request for Prometheus"); - return web::ng::Response{std::move(maybeResponse).value(), request}; + return web::Response{std::move(maybeResponse).value(), request}; } -web::ng::Response +web::Response HealthCheckHandler::operator()( - web::ng::Request const& request, - web::ng::ConnectionMetadata&, + web::Request const& request, + web::ConnectionMetadata&, web::SubscriptionContextPtr, boost::asio::yield_context ) @@ -105,7 +105,7 @@ HealthCheckHandler::operator()( )html"; - return web::ng::Response{boost::beast::http::status::ok, kHEALTH_CHECK_HTML, request}; + return web::Response{boost::beast::http::status::ok, kHEALTH_CHECK_HTML, request}; } } // namespace app diff --git a/src/app/WebHandlers.hpp b/src/app/WebHandlers.hpp index 5dacc222a..35afa2e5c 100644 --- a/src/app/WebHandlers.hpp +++ b/src/app/WebHandlers.hpp @@ -22,11 +22,11 @@ #include "rpc/Errors.hpp" #include "util/log/Logger.hpp" #include "web/AdminVerificationStrategy.hpp" +#include "web/Connection.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" #include "web/SubscriptionContextInterface.hpp" #include "web/dosguard/DOSGuardInterface.hpp" -#include "web/ng/Connection.hpp" -#include "web/ng/Request.hpp" -#include "web/ng/Response.hpp" #include #include @@ -60,8 +60,8 @@ public: * @param connection The connection to check. * @return A response if the connection is not allowed to proceed or void otherwise. */ - std::expected - operator()(web::ng::Connection const& connection); + std::expected + operator()(web::Connection const& connection); }; /** @@ -84,7 +84,7 @@ public: * @param connection The connection which has disconnected. */ void - operator()(web::ng::Connection const& connection); + operator()(web::Connection const& connection); }; /** @@ -108,10 +108,10 @@ public: * @param connectionMetadata The connection metadata. * @return The response to the request. */ - web::ng::Response + web::Response operator()( - web::ng::Request const& request, - web::ng::ConnectionMetadata& connectionMetadata, + web::Request const& request, + web::ConnectionMetadata& connectionMetadata, web::SubscriptionContextPtr, boost::asio::yield_context ); @@ -128,10 +128,10 @@ public: * @param request The request to handle. * @return The response to the request */ - web::ng::Response + web::Response operator()( - web::ng::Request const& request, - web::ng::ConnectionMetadata&, + web::Request const& request, + web::ConnectionMetadata&, web::SubscriptionContextPtr, boost::asio::yield_context ); @@ -169,10 +169,10 @@ public: * @param yield The yield context. * @return The response to the request. */ - web::ng::Response + web::Response operator()( - web::ng::Request const& request, - web::ng::ConnectionMetadata& connectionMetadata, + web::Request const& request, + web::ConnectionMetadata& connectionMetadata, web::SubscriptionContextPtr subscriptionContext, boost::asio::yield_context yield ) @@ -188,7 +188,7 @@ public: try { return rpcHandler_(request, connectionMetadata, std::move(subscriptionContext), yield); } catch (std::exception const&) { - return web::ng::Response{ + return web::Response{ boost::beast::http::status::internal_server_error, rpc::makeError(rpc::RippledError::rpcINTERNAL), request diff --git a/src/main/Main.cpp b/src/main/Main.cpp index 4aebd3c8f..5447d56ca 100644 --- a/src/main/Main.cpp +++ b/src/main/Main.cpp @@ -57,7 +57,7 @@ try { return EXIT_FAILURE; } app::ClioApplication clio{gClioConfig}; - return clio.run(run.useNgWebServer); + return clio.run(); }, [](app::CliArgs::Action::Migrate const& migrate) { if (not app::parseConfig(migrate.configPath)) diff --git a/src/util/config/ConfigDefinition.hpp b/src/util/config/ConfigDefinition.hpp index 558ccb7cb..289446798 100644 --- a/src/util/config/ConfigDefinition.hpp +++ b/src/util/config/ConfigDefinition.hpp @@ -338,7 +338,6 @@ static ClioConfigDefinition gClioConfig = ClioConfigDefinition{ {"server.parallel_requests_limit", ConfigValue{ConfigType::Integer}.optional().withConstraint(gValidateUint16)}, {"server.ws_max_sending_queue_size", ConfigValue{ConfigType::Integer}.defaultValue(1500).withConstraint(gValidateUint32)}, - {"server.__ng_web_server", ConfigValue{ConfigType::Boolean}.defaultValue(false)}, {"prometheus.enabled", ConfigValue{ConfigType::Boolean}.defaultValue(true)}, {"prometheus.compress_reply", ConfigValue{ConfigType::Boolean}.defaultValue(true)}, diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt index 2b62bbe41..2bd0938e5 100644 --- a/src/web/CMakeLists.txt +++ b/src/web/CMakeLists.txt @@ -7,14 +7,14 @@ target_sources( dosguard/IntervalSweepHandler.cpp dosguard/Weights.cpp dosguard/WhitelistHandler.cpp - ng/Connection.cpp - ng/impl/ErrorHandling.cpp - ng/impl/ConnectionHandler.cpp - ng/impl/ServerSslContext.cpp - ng/Request.cpp - ng/Response.cpp - ng/Server.cpp - ng/SubscriptionContext.cpp + Connection.cpp + impl/ErrorHandling.cpp + impl/ConnectionHandler.cpp + impl/ServerSslContext.cpp + Request.cpp + Response.cpp + Server.cpp + SubscriptionContext.cpp Resolver.cpp SubscriptionContext.cpp ) diff --git a/src/web/ng/Connection.cpp b/src/web/Connection.cpp similarity index 95% rename from src/web/ng/Connection.cpp rename to src/web/Connection.cpp index e02fe64b8..5b9e8b500 100644 --- a/src/web/ng/Connection.cpp +++ b/src/web/Connection.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include "web/ng/Connection.hpp" +#include "web/Connection.hpp" #include "util/Taggable.hpp" @@ -26,7 +26,7 @@ #include #include -namespace web::ng { +namespace web { ConnectionMetadata::ConnectionMetadata(std::string ip, util::TagDecoratorFactory const& tagDecoratorFactory) : util::Taggable(tagDecoratorFactory), ip_{std::move(ip)} @@ -54,4 +54,4 @@ Connection::Connection( { } -} // namespace web::ng +} // namespace web diff --git a/src/web/ng/Connection.hpp b/src/web/Connection.hpp similarity index 97% rename from src/web/ng/Connection.hpp rename to src/web/Connection.hpp index 9283e9641..3cd9dc784 100644 --- a/src/web/ng/Connection.hpp +++ b/src/web/Connection.hpp @@ -20,9 +20,9 @@ #pragma once #include "util/Taggable.hpp" -#include "web/ng/Error.hpp" -#include "web/ng/Request.hpp" -#include "web/ng/Response.hpp" +#include "web/Error.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" #include #include @@ -35,7 +35,7 @@ #include #include -namespace web::ng { +namespace web { /** * @brief An interface for a connection metadata class. @@ -159,4 +159,4 @@ public: */ using ConnectionPtr = std::unique_ptr; -} // namespace web::ng +} // namespace web diff --git a/src/web/ng/Error.hpp b/src/web/Error.hpp similarity index 96% rename from src/web/ng/Error.hpp rename to src/web/Error.hpp index 93f356464..d4c8f53be 100644 --- a/src/web/ng/Error.hpp +++ b/src/web/Error.hpp @@ -21,11 +21,11 @@ #include -namespace web::ng { +namespace web { /** * @brief Error of any async operation. */ using Error = boost::system::error_code; -} // namespace web::ng +} // namespace web diff --git a/src/web/HttpSession.hpp b/src/web/HttpSession.hpp deleted file mode 100644 index a60979b23..000000000 --- a/src/web/HttpSession.hpp +++ /dev/null @@ -1,144 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of clio: https://github.com/XRPLF/clio - Copyright (c) 2023, the clio developers. - - Permission to use, copy, modify, and distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#pragma once - -#include "util/Taggable.hpp" -#include "web/AdminVerificationStrategy.hpp" -#include "web/PlainWsSession.hpp" -#include "web/dosguard/DOSGuardInterface.hpp" -#include "web/impl/HttpBase.hpp" -#include "web/interface/Concepts.hpp" -#include "web/interface/ConnectionBase.hpp" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace web { - -using tcp = boost::asio::ip::tcp; - -/** - * @brief Represents a HTTP connection established by a client. - * - * It will handle the upgrade to websocket, pass the ownership of the socket to the upgrade session. - * Otherwise, it will pass control to the base class. - * - * @tparam HandlerType The type of the server handler to use - */ -template -class HttpSession : public impl::HttpBase, - public std::enable_shared_from_this> { - boost::beast::tcp_stream stream_; - std::reference_wrapper tagFactory_; - std::uint32_t maxWsSendingQueueSize_; - -public: - /** - * @brief Create a new session. - * - * @param socket The socket. Ownership is transferred to HttpSession - * @param ip Client's IP address - * @param adminVerification The admin verification strategy to use - * @param tagFactory A factory that is used to generate tags to track requests and sessions - * @param dosGuard The denial of service guard to use - * @param handler The server handler to use - * @param buffer Buffer with initial data received from the peer - * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket - */ - explicit HttpSession( - tcp::socket&& socket, - std::string const& ip, - std::shared_ptr const& adminVerification, - std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, - std::shared_ptr const& handler, - boost::beast::flat_buffer buffer, - std::uint32_t maxWsSendingQueueSize - ) - : impl::HttpBase( - ip, - tagFactory, - adminVerification, - dosGuard, - handler, - std::move(buffer) - ) - , stream_(std::move(socket)) - , tagFactory_(tagFactory) - , maxWsSendingQueueSize_(maxWsSendingQueueSize) - { - } - - ~HttpSession() override = default; - - /** @return The TCP stream */ - boost::beast::tcp_stream& - stream() - { - return stream_; - } - - /** @brief Starts reading from the stream. */ - void - run() - { - boost::asio::dispatch( - stream_.get_executor(), - boost::beast::bind_front_handler( - &impl::HttpBase::doRead, this->shared_from_this() - ) - ); - } - - /** @brief Closes the underlying socket. */ - void - doClose() - { - boost::beast::error_code ec; - stream_.socket().shutdown(tcp::socket::shutdown_send, ec); - } - - /** @brief Upgrade to WebSocket connection. */ - void - upgrade() - { - std::make_shared>( - std::move(stream_), - this->clientIp, - tagFactory_, - this->dosGuard_, - this->handler_, - std::move(this->buffer_), - std::move(this->req_), - ConnectionBase::isAdmin(), - maxWsSendingQueueSize_ - ) - ->run(); - } -}; -} // namespace web diff --git a/src/web/ng/MessageHandler.hpp b/src/web/MessageHandler.hpp similarity index 90% rename from src/web/ng/MessageHandler.hpp rename to src/web/MessageHandler.hpp index 6b3224c31..ec2f0a1a9 100644 --- a/src/web/ng/MessageHandler.hpp +++ b/src/web/MessageHandler.hpp @@ -19,16 +19,16 @@ #pragma once +#include "web/Connection.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" #include "web/SubscriptionContextInterface.hpp" -#include "web/ng/Connection.hpp" -#include "web/ng/Request.hpp" -#include "web/ng/Response.hpp" #include #include -namespace web::ng { +namespace web { /** * @brief Handler for messages. @@ -36,4 +36,4 @@ namespace web::ng { using MessageHandler = std::function; -} // namespace web::ng +} // namespace web diff --git a/src/web/PlainWsSession.hpp b/src/web/PlainWsSession.hpp deleted file mode 100644 index 6066765a1..000000000 --- a/src/web/PlainWsSession.hpp +++ /dev/null @@ -1,204 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of clio: https://github.com/XRPLF/clio - Copyright (c) 2023, the clio developers. - - Permission to use, copy, modify, and distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#pragma once - -#include "util/Taggable.hpp" -#include "web/dosguard/DOSGuardInterface.hpp" -#include "web/impl/WsBase.hpp" -#include "web/interface/ConnectionBase.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace web { - -/** - * @brief Represents a non-secure websocket session. - * - * Majority of the operations are handled by the base class. - */ -template -class PlainWsSession : public impl::WsBase { - using StreamType = boost::beast::websocket::stream; - StreamType ws_; - -public: - /** - * @brief Create a new non-secure websocket session. - * - * @param socket The socket. Ownership is transferred - * @param ip Client's IP address - * @param tagFactory A factory that is used to generate tags to track requests and sessions - * @param dosGuard The denial of service guard to use - * @param handler The server handler to use - * @param buffer Buffer with initial data received from the peer - * @param isAdmin Whether the connection has admin privileges, - * @param maxSendingQueueSize The maximum size of the sending queue for websocket - */ - explicit PlainWsSession( - boost::asio::ip::tcp::socket&& socket, - std::string ip, - std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, - std::shared_ptr const& handler, - boost::beast::flat_buffer&& buffer, - bool isAdmin, - std::uint32_t maxSendingQueueSize - ) - : impl::WsBase( - ip, - tagFactory, - dosGuard, - handler, - std::move(buffer), - maxSendingQueueSize - ) - , ws_(std::move(socket)) - { - ConnectionBase::isAdmin_ = isAdmin; // NOLINT(cppcoreguidelines-prefer-member-initializer) - } - - ~PlainWsSession() override = default; - - /** @return The websocket stream. */ - StreamType& - ws() - { - return ws_; - } -}; - -/** - * @brief The websocket upgrader class, upgrade from an HTTP session to a non-secure websocket session. - * - * Pass the socket to the session class after upgrade. - */ -template -class WsUpgrader : public std::enable_shared_from_this> { - using std::enable_shared_from_this>::shared_from_this; - - boost::beast::tcp_stream http_; - boost::optional> parser_; - boost::beast::flat_buffer buffer_; - std::reference_wrapper tagFactory_; - std::reference_wrapper dosGuard_; - http::request req_; - std::string ip_; - std::shared_ptr const handler_; - bool isAdmin_; - std::uint32_t maxWsSendingQueueSize_; - -public: - /** - * @brief Create a new upgrader to non-secure websocket. - * - * @param stream The TCP stream. Ownership is transferred - * @param ip Client's IP address - * @param tagFactory A factory that is used to generate tags to track requests and sessions - * @param dosGuard The denial of service guard to use - * @param handler The server handler to use - * @param buffer Buffer with initial data received from the peer. Ownership is transferred - * @param request The request. Ownership is transferred - * @param isAdmin Whether the connection has admin privileges - * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket - */ - WsUpgrader( - boost::beast::tcp_stream&& stream, - std::string ip, - std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, - std::shared_ptr const& handler, - boost::beast::flat_buffer&& buffer, - http::request request, - bool isAdmin, - std::uint32_t maxWsSendingQueueSize - ) - : http_(std::move(stream)) - , buffer_(std::move(buffer)) - , tagFactory_(tagFactory) - , dosGuard_(dosGuard) - , req_(std::move(request)) - , ip_(std::move(ip)) - , handler_(handler) - , isAdmin_(isAdmin) - , maxWsSendingQueueSize_(maxWsSendingQueueSize) - { - } - - /** @brief Initiate the upgrade. */ - void - run() - { - boost::asio::dispatch( - http_.get_executor(), - boost::beast::bind_front_handler(&WsUpgrader::doUpgrade, shared_from_this()) - ); - } - -private: - void - doUpgrade() - { - parser_.emplace(); - - static constexpr auto kMAX_BODY_SIZE = 10000; - parser_->body_limit(kMAX_BODY_SIZE); - - boost::beast::get_lowest_layer(http_).expires_after(std::chrono::seconds(30)); - onUpgrade(); - } - - void - onUpgrade() - { - if (!boost::beast::websocket::is_upgrade(req_)) - return; - - // Disable the timeout. The websocket::stream uses its own timeout settings. - boost::beast::get_lowest_layer(http_).expires_never(); - - std::make_shared>( - http_.release_socket(), - ip_, - tagFactory_, - dosGuard_, - handler_, - std::move(buffer_), - isAdmin_, - maxWsSendingQueueSize_ - ) - ->run(std::move(req_)); - } -}; - -} // namespace web diff --git a/src/web/ng/ProcessingPolicy.hpp b/src/web/ProcessingPolicy.hpp similarity index 96% rename from src/web/ng/ProcessingPolicy.hpp rename to src/web/ProcessingPolicy.hpp index ad69a7444..d336866da 100644 --- a/src/web/ng/ProcessingPolicy.hpp +++ b/src/web/ProcessingPolicy.hpp @@ -19,11 +19,11 @@ #pragma once -namespace web::ng { +namespace web { /** * @brief Requests processing policy. */ enum class ProcessingPolicy { Sequential, Parallel }; -} // namespace web::ng +} // namespace web diff --git a/src/web/README.md b/src/web/README.md index 76b0ce41e..cf7768e2c 100644 --- a/src/web/README.md +++ b/src/web/README.md @@ -1,15 +1,67 @@ -# Web server subsystem +# Web Server Subsystem -This folder contains all of the classes for running the web server. +This folder contains all of the classes for running Clio's web server. + +## Overview The web server subsystem: -- Handles JSON-RPC and websocket requests. +- Handles JSON-RPC requests over HTTP and WebSocket connections +- Supports SSL/TLS encryption if certificate and key files are specified in the config +- Processes all types of requests on a single port +- Implements asynchronous request handling using [Boost Asio](https://www.boost.org/doc/libs/1_83_0/doc/html/boost_asio.html) +- Provides request rate limiting through a built-in Denial-of-Service (DoS) Guard mechanism +- Supports both sequential and parallel request processing policies -- Supports SSL if a cert and key file are specified in the config. +## Key Components -- Handles all types of requests on a single port. +### Core Components -Each request is handled asynchronously using [Boost Asio](https://www.boost.org/doc/libs/1_82_0/doc/html/boost_asio.html). +- **Server** (`Server.hpp/cpp`): The main web server class that manages connections and routes requests +- **Connection** (`Connection.hpp/cpp`): Represents a client connection and provides an abstraction layer over HTTP and WebSocket connections +- **Request/Response** (`Request.hpp/cpp`, `Response.hpp/cpp`): Classes for handling HTTP requests and responses +- **MessageHandler** (`MessageHandler.hpp`): An interface for handling different types of messages (e.g., HTTP GET/POST, WebSocket) +- **RPCServerHandler** (`RPCServerHandler.hpp`): Handles RPC requests and integrates with the RPC engine -Much of this code was originally copied from Boost beast example code. +### Connection Processing + +- **ConnectionHandler** (`impl/ConnectionHandler.hpp/cpp`): Manages the lifecycle of connections and processes requests +- **ProcessingPolicy** (`ProcessingPolicy.hpp`): Defines whether requests are processed sequentially or in parallel +- **HttpConnection/WsConnection** (`impl/HttpConnection.hpp`, `impl/WsConnection.hpp`): Concrete implementations for HTTP and WebSocket connections + +### Security Features + +- **DOSGuard** (`dosguard/DOSGuard.hpp/cpp`): Denial-of-Service protection system that implements rate limiting +- **IntervalSweepHandler** (`dosguard/IntervalSweepHandler.hpp/cpp`): Periodically clears DoS guard state +- **WhitelistHandler** (`dosguard/WhitelistHandler.hpp/cpp`): Manages IP address whitelisting for bypass of rate limits +- **AdminVerificationStrategy** (`AdminVerificationStrategy.hpp/cpp`): Handles verification of admin privileges + +### Subscription + +- **SubscriptionContext** (`SubscriptionContext.hpp/cpp`): Manages WebSocket subscriptions for streaming updates + +## Architecture + +The server design uses the following patterns: + +- **RAII**: Resource management through C++ RAII principles +- **Dependency Injection**: Components accept their dependencies through constructor parameters +- **Interface-based design**: Components depend on interfaces rather than concrete implementations +- **Asynchronous programming**: Uses Boost Asio for non-blocking I/O operations with coroutines + +Each incoming request is handled asynchronously, with the processing being dispatched to appropriate handlers based on the request type (GET, POST, WebSocket). The server supports both secure (SSL/TLS) and non-secure connections based on configuration. + +## SSL Support + +The server creates an SSL context if certificate and key files are specified in the configuration. When SSL is enabled, all connections are encrypted. + +## Request Flow + +1. Client connects to the server +2. Server performs security checks (e.g., DoS Guard, admin verification if needed) +3. Server reads the request asynchronously +4. Request is routed to appropriate handler based on HTTP method and target +5. Handler processes the request and generates a response +6. Response is sent back to the client +7. For persistent connections, the server returns to step 3 +8. When the client disconnects or an error occurs, the connection is closed diff --git a/src/web/RPCServerHandler.hpp b/src/web/RPCServerHandler.hpp index 851f62a7f..a13f00622 100644 --- a/src/web/RPCServerHandler.hpp +++ b/src/web/RPCServerHandler.hpp @@ -26,17 +26,23 @@ #include "rpc/JS.hpp" #include "rpc/RPCHelpers.hpp" #include "rpc/common/impl/APIVersionParser.hpp" +#include "util/Assert.hpp" +#include "util/CoroutineGroup.hpp" #include "util/JsonUtils.hpp" #include "util/Profiler.hpp" #include "util/Taggable.hpp" -#include "util/config/ConfigDefinition.hpp" #include "util/log/Logger.hpp" +#include "web/Connection.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" +#include "web/SubscriptionContextInterface.hpp" #include "web/dosguard/DOSGuardInterface.hpp" #include "web/impl/ErrorHandling.hpp" -#include "web/interface/ConnectionBase.hpp" #include +#include #include +#include #include #include #include @@ -48,8 +54,8 @@ #include #include #include +#include #include -#include #include #include @@ -65,9 +71,9 @@ class RPCServerHandler { std::shared_ptr const backend_; std::shared_ptr const rpcEngine_; std::shared_ptr const etl_; + std::reference_wrapper dosguard_; util::TagDecoratorFactory const tagFactory_; rpc::impl::ProductionAPIVersionParser apiVersionParser_; // can be injected if needed - std::reference_wrapper dosguard_; util::Logger log_{"RPC"}; util::Logger perfLog_{"Performance"}; @@ -87,14 +93,14 @@ public: std::shared_ptr const& backend, std::shared_ptr const& rpcEngine, std::shared_ptr const& etl, - web::dosguard::DOSGuardInterface& dosguard + dosguard::DOSGuardInterface& dosguard ) : backend_(backend) , rpcEngine_(rpcEngine) , etl_(etl) + , dosguard_(dosguard) , tagFactory_(config) , apiVersionParser_(config.getObject("api_version")) - , dosguard_(dosguard) { } @@ -102,123 +108,165 @@ public: * @brief The callback when server receives a request. * * @param request The request - * @param connection The connection + * @param connectionMetadata The connection metadata + * @param subscriptionContext The subscription context + * @param yield The yield context + * @return The response */ - void - operator()(std::string const& request, std::shared_ptr const& connection) + [[nodiscard]] Response + operator()( + Request const& request, + ConnectionMetadata const& connectionMetadata, + SubscriptionContextPtr subscriptionContext, + boost::asio::yield_context yield + ) { - if (not dosguard_.get().isOk(connection->clientIp)) { - connection->sendSlowDown(request); - return; + if (not dosguard_.get().isOk(connectionMetadata.ip())) { + return makeSlowDownResponse(request, std::nullopt); } - try { - auto req = boost::json::parse(request).as_object(); - LOG(perfLog_.debug()) << connection->tag() << "Adding to work queue"; + std::optional response; + util::CoroutineGroup coroutineGroup{yield, 1}; + auto const onTaskComplete = coroutineGroup.registerForeign(yield); + ASSERT(onTaskComplete.has_value(), "Coroutine group can't be full"); - if (not connection->upgraded and shouldReplaceParams(req)) - req[JS(params)] = boost::json::array({boost::json::object{}}); + bool const postSuccessful = rpcEngine_->post( + [this, + &request, + &response, + &onTaskComplete = onTaskComplete.value(), + &connectionMetadata, + subscriptionContext = std::move(subscriptionContext)](boost::asio::yield_context innerYield) mutable { + try { + boost::system::error_code ec; + auto parsedRequest = boost::json::parse(request.message(), ec); + if (ec.failed() or not parsedRequest.is_object()) { + rpcEngine_->notifyBadSyntax(); + response = impl::ErrorHelper{request}.makeJsonParsingError(); + if (ec.failed()) { + LOG(log_.warn()) + << "Error parsing JSON: " << ec.message() << ". For request: " << request.message(); + } else { + LOG(log_.warn()) << "Received not a JSON object. For request: " << request.message(); + } + } else { + auto parsedObject = std::move(parsedRequest).as_object(); - if (not dosguard_.get().request(connection->clientIp, req)) { - connection->sendSlowDown(request); - return; - } + if (not dosguard_.get().request(connectionMetadata.ip(), parsedObject)) { + response = makeSlowDownResponse(request, parsedObject); + } else { + LOG(perfLog_.debug()) << connectionMetadata.tag() << "Adding to work queue"; - if (!rpcEngine_->post( - [this, request = std::move(req), connection](boost::asio::yield_context yield) mutable { - handleRequest(yield, std::move(request), connection); - }, - connection->clientIp - )) { - rpcEngine_->notifyTooBusy(); - web::impl::ErrorHelper(connection).sendTooBusyError(); - } - } catch (boost::system::system_error const& ex) { - // system_error thrown when json parsing failed - rpcEngine_->notifyBadSyntax(); - web::impl::ErrorHelper(connection).sendJsonParsingError(); - LOG(log_.warn()) << "Error parsing JSON: " << ex.what() << ". For request: " << request; - } catch (std::invalid_argument const& ex) { - // thrown when json parses something that is not an object at top level - rpcEngine_->notifyBadSyntax(); - LOG(log_.warn()) << "Invalid argument error: " << ex.what() << ". For request: " << request; - web::impl::ErrorHelper(connection).sendJsonParsingError(); - } catch (std::exception const& ex) { - LOG(perfLog_.error()) << connection->tag() << "Caught exception: " << ex.what(); - rpcEngine_->notifyInternalError(); - throw; + if (not connectionMetadata.wasUpgraded() and shouldReplaceParams(parsedObject)) + parsedObject[JS(params)] = boost::json::array({boost::json::object{}}); + + response = handleRequest( + innerYield, + request, + std::move(parsedObject), + connectionMetadata, + std::move(subscriptionContext) + ); + } + } + } catch (std::exception const& ex) { + LOG(perfLog_.error()) << connectionMetadata.tag() << "Caught exception: " << ex.what(); + rpcEngine_->notifyInternalError(); + response = impl::ErrorHelper{request}.makeInternalError(); + } + + // notify the coroutine group that the foreign task is done + onTaskComplete(); + }, + connectionMetadata.ip() + ); + + if (not postSuccessful) { + // onTaskComplete must be called to notify coroutineGroup that the foreign task is done + onTaskComplete->operator()(); + rpcEngine_->notifyTooBusy(); + return impl::ErrorHelper{request}.makeTooBusyError(); } + + // Put the coroutine to sleep until the foreign task is done + coroutineGroup.asyncWait(yield); + ASSERT(response.has_value(), "Woke up coroutine without setting response"); + + if (not dosguard_.get().add(connectionMetadata.ip(), response->message().size())) { + response->setMessage(makeLoadWarning(*response)); + } + + return std::move(response).value(); } private: - void + Response handleRequest( boost::asio::yield_context yield, + Request const& rawRequest, boost::json::object&& request, - std::shared_ptr const& connection + ConnectionMetadata const& connectionMetadata, + SubscriptionContextPtr subscriptionContext ) { - LOG(log_.info()) << connection->tag() << (connection->upgraded ? "ws" : "http") + LOG(log_.info()) << connectionMetadata.tag() << (connectionMetadata.wasUpgraded() ? "ws" : "http") << " received request from work queue: " << util::removeSecret(request) - << " ip = " << connection->clientIp; + << " ip = " << connectionMetadata.ip(); try { auto const range = backend_->fetchLedgerRange(); if (!range) { // for error that happened before the handler, we don't attach any warnings rpcEngine_->notifyNotReady(); - web::impl::ErrorHelper(connection, std::move(request)).sendNotReadyError(); - - return; + return impl::ErrorHelper{rawRequest, std::move(request)}.makeNotReadyError(); } auto const context = [&] { - if (connection->upgraded) { + if (connectionMetadata.wasUpgraded()) { + ASSERT(subscriptionContext != nullptr, "Subscription context must exist for a WS connection"); return rpc::makeWsContext( yield, request, - connection->makeSubscriptionContext(tagFactory_), - tagFactory_.with(connection->tag()), + std::move(subscriptionContext), + tagFactory_.with(connectionMetadata.tag()), *range, - connection->clientIp, + connectionMetadata.ip(), std::cref(apiVersionParser_), - connection->isAdmin() + connectionMetadata.isAdmin() ); } return rpc::makeHttpContext( yield, request, - tagFactory_.with(connection->tag()), + tagFactory_.with(connectionMetadata.tag()), *range, - connection->clientIp, + connectionMetadata.ip(), std::cref(apiVersionParser_), - connection->isAdmin() + connectionMetadata.isAdmin() ); }(); if (!context) { auto const err = context.error(); - LOG(perfLog_.warn()) << connection->tag() << "Could not create Web context: " << err; - LOG(log_.warn()) << connection->tag() << "Could not create Web context: " << err; + LOG(perfLog_.warn()) << connectionMetadata.tag() << "Could not create Web context: " << err; + LOG(log_.warn()) << connectionMetadata.tag() << "Could not create Web context: " << err; // we count all those as BadSyntax - as the WS path would. // Although over HTTP these will yield a 400 status with a plain text response (for most). rpcEngine_->notifyBadSyntax(); - web::impl::ErrorHelper(connection, std::move(request)).sendError(err); - - return; + return impl::ErrorHelper(rawRequest, std::move(request)).makeError(err); } auto [result, timeDiff] = util::timed([&]() { return rpcEngine_->buildResponse(*context); }); - auto const us = std::chrono::duration(timeDiff); + auto us = std::chrono::duration(timeDiff); rpc::logDuration(request, context->tag(), us); boost::json::object response; if (!result.response.has_value()) { // note: error statuses are counted/notified in buildResponse itself - response = web::impl::ErrorHelper(connection, request).composeError(result.response.error()); + response = impl::ErrorHelper(rawRequest, request).composeError(result.response.error()); auto const responseStr = boost::json::serialize(response); LOG(perfLog_.debug()) << context->tag() << "Encountered error: " << responseStr; @@ -237,7 +285,7 @@ private: // if the result is forwarded - just use it as is // if forwarded request has error, for http, error should be in "result"; for ws, error should // be at top - if (isForwarded && (json.contains(JS(result)) || connection->upgraded)) { + if (isForwarded && (json.contains(JS(result)) || connectionMetadata.wasUpgraded())) { for (auto const& [k, v] : json) response.insert_or_assign(k, v); } else { @@ -249,7 +297,7 @@ private: // for ws there is an additional field "status" in the response, // otherwise the "status" is in the "result" field - if (connection->upgraded) { + if (connectionMetadata.wasUpgraded()) { auto const appendFieldIfExist = [&](auto const& field) { if (request.contains(field) and not request.at(field).is_null()) response[field] = request.at(field); @@ -275,20 +323,51 @@ private: warnings.emplace_back(rpc::makeWarning(rpc::WarnRpcOutdated)); response["warnings"] = warnings; - connection->send(boost::json::serialize(response)); + return Response{boost::beast::http::status::ok, response, rawRequest}; } catch (std::exception const& ex) { // note: while we are catching this in buildResponse too, this is here to make sure // that any other code that may throw is outside of buildResponse is also worked around. - LOG(perfLog_.error()) << connection->tag() << "Caught exception: " << ex.what(); - LOG(log_.error()) << connection->tag() << "Caught exception: " << ex.what(); + LOG(perfLog_.error()) << connectionMetadata.tag() << "Caught exception: " << ex.what(); + LOG(log_.error()) << connectionMetadata.tag() << "Caught exception: " << ex.what(); rpcEngine_->notifyInternalError(); - web::impl::ErrorHelper(connection, std::move(request)).sendInternalError(); - - return; + return impl::ErrorHelper(rawRequest, std::move(request)).makeInternalError(); } } + static Response + makeSlowDownResponse(Request const& request, std::optional requestJson) + { + auto error = rpc::makeError(rpc::RippledError::rpcSLOW_DOWN); + + if (not request.isHttp()) { + try { + if (not requestJson.has_value()) { + requestJson = boost::json::parse(request.message()); + } + if (requestJson->is_object() && requestJson->as_object().contains("id")) + error["id"] = requestJson->as_object().at("id"); + error["request"] = request.message(); + } catch (std::exception const&) { + error["request"] = request.message(); + } + } + return web::Response{boost::beast::http::status::service_unavailable, error, request}; + } + + static boost::json::object + makeLoadWarning(Response const& response) + { + auto jsonResponse = boost::json::parse(response.message()).as_object(); + jsonResponse["warning"] = "load"; + if (jsonResponse.contains("warnings") && jsonResponse["warnings"].is_array()) { + jsonResponse["warnings"].as_array().push_back(rpc::makeWarning(rpc::WarnRpcRateLimit)); + } else { + jsonResponse["warnings"] = boost::json::array{rpc::makeWarning(rpc::WarnRpcRateLimit)}; + } + return jsonResponse; + } + bool shouldReplaceParams(boost::json::object const& req) const { diff --git a/src/web/ng/Request.cpp b/src/web/Request.cpp similarity index 98% rename from src/web/ng/Request.cpp rename to src/web/Request.cpp index 9bf1c9a41..a9a7385c4 100644 --- a/src/web/ng/Request.cpp +++ b/src/web/Request.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include "web/ng/Request.hpp" +#include "web/Request.hpp" #include "util/OverloadSet.hpp" @@ -33,7 +33,7 @@ #include #include -namespace web::ng { +namespace web { namespace { @@ -142,4 +142,4 @@ Request::httpRequest() const return std::get(data_); } -} // namespace web::ng +} // namespace web diff --git a/src/web/ng/Request.hpp b/src/web/Request.hpp similarity index 99% rename from src/web/ng/Request.hpp rename to src/web/Request.hpp index 55f4ff560..5ece22ca7 100644 --- a/src/web/ng/Request.hpp +++ b/src/web/Request.hpp @@ -29,7 +29,7 @@ #include #include -namespace web::ng { +namespace web { /** * @brief Represents an HTTP or WebSocket request. @@ -150,4 +150,4 @@ private: httpRequest() const; }; -} // namespace web::ng +} // namespace web diff --git a/src/web/ng/Response.cpp b/src/web/Response.cpp similarity index 97% rename from src/web/ng/Response.cpp rename to src/web/Response.cpp index 94f40278a..08b4cb209 100644 --- a/src/web/ng/Response.cpp +++ b/src/web/Response.cpp @@ -17,13 +17,13 @@ */ //============================================================================== -#include "web/ng/Response.hpp" +#include "web/Response.hpp" #include "util/Assert.hpp" #include "util/OverloadSet.hpp" #include "util/build/Build.hpp" -#include "web/ng/Connection.hpp" -#include "web/ng/Request.hpp" +#include "web/Connection.hpp" +#include "web/Request.hpp" #include #include @@ -43,7 +43,7 @@ namespace http = boost::beast::http; -namespace web::ng { +namespace web { namespace { @@ -193,4 +193,4 @@ Response::asWsResponse() const& return boost::asio::buffer(message.data(), message.size()); } -} // namespace web::ng +} // namespace web diff --git a/src/web/ng/Response.hpp b/src/web/Response.hpp similarity index 98% rename from src/web/ng/Response.hpp rename to src/web/Response.hpp index 063249519..7deeeb74e 100644 --- a/src/web/ng/Response.hpp +++ b/src/web/Response.hpp @@ -19,7 +19,7 @@ #pragma once -#include "web/ng/Request.hpp" +#include "web/Request.hpp" #include #include @@ -30,7 +30,7 @@ #include #include -namespace web::ng { +namespace web { class Connection; @@ -133,4 +133,4 @@ public: asWsResponse() const&; }; -} // namespace web::ng +} // namespace web diff --git a/src/web/ng/Server.cpp b/src/web/Server.cpp similarity index 97% rename from src/web/ng/Server.cpp rename to src/web/Server.cpp index a8bb6477e..4790295c9 100644 --- a/src/web/ng/Server.cpp +++ b/src/web/Server.cpp @@ -17,19 +17,19 @@ */ //============================================================================== -#include "web/ng/Server.hpp" +#include "web/Server.hpp" #include "util/Assert.hpp" #include "util/Taggable.hpp" #include "util/config/ConfigDefinition.hpp" #include "util/config/ObjectView.hpp" #include "util/log/Logger.hpp" -#include "web/ng/Connection.hpp" -#include "web/ng/MessageHandler.hpp" -#include "web/ng/ProcessingPolicy.hpp" -#include "web/ng/Response.hpp" -#include "web/ng/impl/HttpConnection.hpp" -#include "web/ng/impl/ServerSslContext.hpp" +#include "web/Connection.hpp" +#include "web/MessageHandler.hpp" +#include "web/ProcessingPolicy.hpp" +#include "web/Response.hpp" +#include "web/impl/HttpConnection.hpp" +#include "web/impl/ServerSslContext.hpp" #include #include @@ -54,7 +54,7 @@ #include #include -namespace web::ng { +namespace web { namespace { @@ -316,7 +316,7 @@ Server::handleConnection(boost::asio::ip::tcp::socket socket, boost::asio::yield boost::asio::spawn( ctx_.get(), [connection = std::move(connectionExpected).value()](boost::asio::yield_context yield) { - web::ng::impl::ConnectionHandler::stopConnection(*connection, yield); + web::impl::ConnectionHandler::stopConnection(*connection, yield); } ); return; @@ -382,4 +382,4 @@ makeServer( }; } -} // namespace web::ng +} // namespace web diff --git a/src/web/Server.hpp b/src/web/Server.hpp index b480b322f..180d7428c 100644 --- a/src/web/Server.hpp +++ b/src/web/Server.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ /* This file is part of clio: https://github.com/XRPLF/clio - Copyright (c) 2023, the clio developers. + Copyright (c) 2024, the clio developers. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -20,363 +20,173 @@ #pragma once #include "util/Taggable.hpp" +#include "util/config/ConfigDefinition.hpp" #include "util/log/Logger.hpp" -#include "web/AdminVerificationStrategy.hpp" -#include "web/HttpSession.hpp" -#include "web/SslHttpSession.hpp" -#include "web/dosguard/DOSGuardInterface.hpp" -#include "web/interface/Concepts.hpp" -#include "web/ng/impl/ServerSslContext.hpp" +#include "web/Connection.hpp" +#include "web/MessageHandler.hpp" +#include "web/ProcessingPolicy.hpp" +#include "web/Response.hpp" +#include "web/impl/ConnectionHandler.hpp" #include -#include #include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include #include -#include #include -#include -/** - * @brief This namespace implements the web server and related components. - * - * The web server is leveraging the power of `boost::asio` with it's coroutine support thru `boost::asio::yield_context` - * and `boost::asio::spawn`. - * - * Majority of the code is based on examples that came with boost. - */ namespace web { /** - * @brief The Detector class to detect if the connection is a ssl or not. - * - * If it is an SSL connection, the Detector will pass the ownership of the socket to SslSessionType, otherwise to - * PlainSessionType. - * - * @tparam PlainSessionType The plain session type - * @tparam SslSessionType The SSL session type - * @tparam HandlerType The executor to handle the requests + * @brief A tag class for server to help identify Server in templated code. */ -template < - template class PlainSessionType, - template class SslSessionType, - SomeServerHandler HandlerType> -class Detector : public std::enable_shared_from_this> { - using std::enable_shared_from_this>::shared_from_this; - - util::Logger log_{"WebServer"}; - boost::beast::tcp_stream stream_; - std::optional> ctx_; - std::reference_wrapper tagFactory_; - std::reference_wrapper const dosGuard_; - std::shared_ptr const handler_; - boost::beast::flat_buffer buffer_; - std::shared_ptr const adminVerification_; - std::uint32_t maxWsSendingQueueSize_; - -public: - /** - * @brief Create a new detector. - * - * @param socket The socket. Ownership is transferred - * @param ctx The SSL context if any - * @param tagFactory A factory that is used to generate tags to track requests and sessions - * @param dosGuard The denial of service guard to use - * @param handler The server handler to use - * @param adminVerification The admin verification strategy to use - * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket - */ - Detector( - tcp::socket&& socket, - std::optional> ctx, - std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, - std::shared_ptr handler, - std::shared_ptr adminVerification, - std::uint32_t maxWsSendingQueueSize - ) - : stream_(std::move(socket)) - , ctx_(ctx) - , tagFactory_(std::cref(tagFactory)) - , dosGuard_(dosGuard) - , handler_(std::move(handler)) - , adminVerification_(std::move(adminVerification)) - , maxWsSendingQueueSize_(maxWsSendingQueueSize) - { - } - - /** - * @brief A helper function that is called when any error occurs. - * - * @param ec The error code - * @param message The message to include in the log - */ - void - fail(boost::system::error_code ec, char const* message) - { - if (ec == boost::asio::ssl::error::stream_truncated) - return; - - LOG(log_.info()) << "Detector failed (" << message << "): " << ec.message(); - } - - /** @brief Initiate the detection. */ - void - run() - { - boost::beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); - async_detect_ssl(stream_, buffer_, boost::beast::bind_front_handler(&Detector::onDetect, shared_from_this())); - } - - /** - * @brief Handles detection result. - * - * @param ec The error code - * @param result true if SSL is detected; false otherwise - */ - void - onDetect(boost::beast::error_code ec, bool result) - { - if (ec) - return fail(ec, "detect"); - - std::string ip; - try { - ip = stream_.socket().remote_endpoint().address().to_string(); - } catch (std::exception const&) { - return fail(ec, "cannot get remote endpoint"); - } - - if (result) { - if (!ctx_) - return fail(ec, "SSL is not supported by this server"); - - std::make_shared>( - stream_.release_socket(), - ip, - adminVerification_, - *ctx_, - tagFactory_, - dosGuard_, - handler_, - std::move(buffer_), - maxWsSendingQueueSize_ - ) - ->run(); - return; - } - - std::make_shared>( - stream_.release_socket(), - ip, - adminVerification_, - tagFactory_, - dosGuard_, - handler_, - std::move(buffer_), - maxWsSendingQueueSize_ - ) - ->run(); - } +struct ServerTag { + virtual ~ServerTag() = default; }; -/** - * @brief The WebServer class. It creates server socket and start listening on it. - * - * Once there is client connection, it will accept it and pass the socket to Detector to detect ssl or plain. - * - * @tparam PlainSessionType The plain session to handle non-ssl connection. - * @tparam SslSessionType The SSL session to handle SSL connection. - * @tparam HandlerType The handler to process the request and return response. - */ -template < - template class PlainSessionType, - template class SslSessionType, - SomeServerHandler HandlerType> -class Server : public std::enable_shared_from_this> { - using std::enable_shared_from_this>::shared_from_this; +template +concept SomeServer = std::derived_from; +/** + * @brief Web server class. + */ +class Server : public ServerTag { +public: + /** + * @brief Check to perform for each new client connection. The check takes client ip as input and returns a Response + * if the check failed. Response will be sent to the client and the connection will be closed. + */ + using OnConnectCheck = std::function(Connection const&)>; + + /** + * @brief Hook called when any connection disconnects + */ + using OnDisconnectHook = impl::ConnectionHandler::OnDisconnectHook; + +private: util::Logger log_{"WebServer"}; - std::reference_wrapper ioc_; - std::optional ctx_; - util::TagDecoratorFactory tagFactory_; - std::reference_wrapper dosGuard_; - std::shared_ptr handler_; - tcp::acceptor acceptor_; - std::shared_ptr adminVerification_; - std::uint32_t maxWsSendingQueueSize_; + util::Logger perfLog_{"Performance"}; + + std::reference_wrapper ctx_; + std::optional sslContext_; + + util::TagDecoratorFactory tagDecoratorFactory_; + + impl::ConnectionHandler connectionHandler_; + boost::asio::ip::tcp::endpoint endpoint_; + + OnConnectCheck onConnectCheck_; + + bool running_{false}; public: /** - * @brief Create a new instance of the web server. + * @brief Construct a new Server object. * - * @param ioc The io_context to run the server on - * @param ctx The SSL context if any - * @param endpoint The endpoint to listen on - * @param tagFactory A factory that is used to generate tags to track requests and sessions - * @param dosGuard The denial of service guard to use - * @param handler The server handler to use - * @param adminVerification The admin verification strategy to use - * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket + * @param ctx The boost::asio::io_context to use. + * @param endpoint The endpoint to listen on. + * @param sslContext The SSL context to use (optional). + * @param processingPolicy The requests processing policy (parallel or sequential). + * @param parallelRequestLimit The limit of requests for one connection that can be processed in parallel. Only used + * if processingPolicy is parallel. + * @param tagDecoratorFactory The tag decorator factory. + * @param maxSubscriptionSendQueueSize The maximum size of the subscription send queue. + * @param onConnectCheck The check to perform on each connection. + * @param onDisconnectHook The hook to call on each disconnection. */ Server( - boost::asio::io_context& ioc, - std::optional ctx, - tcp::endpoint endpoint, - util::TagDecoratorFactory tagFactory, - dosguard::DOSGuardInterface& dosGuard, - std::shared_ptr handler, - std::shared_ptr adminVerification, - std::uint32_t maxWsSendingQueueSize - ) - : ioc_(std::ref(ioc)) - , ctx_(std::move(ctx)) - , tagFactory_(tagFactory) - , dosGuard_(std::ref(dosGuard)) - , handler_(std::move(handler)) - , acceptor_(boost::asio::make_strand(ioc)) - , adminVerification_(std::move(adminVerification)) - , maxWsSendingQueueSize_(maxWsSendingQueueSize) - { - boost::beast::error_code ec; + boost::asio::io_context& ctx, + boost::asio::ip::tcp::endpoint endpoint, + std::optional sslContext, + ProcessingPolicy processingPolicy, + std::optional parallelRequestLimit, + util::TagDecoratorFactory tagDecoratorFactory, + std::optional maxSubscriptionSendQueueSize, + OnConnectCheck onConnectCheck, + OnDisconnectHook onDisconnectHook + ); - acceptor_.open(endpoint.protocol(), ec); - if (ec) - return; + /** + * @brief Copy constructor is deleted. The Server couldn't be copied. + */ + Server(Server const&) = delete; - acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec); - if (ec) - return; + /** + * @brief Move constructor is deleted because connectionHandler_ contains references to some fields of the Server. + */ + Server(Server&&) = delete; - acceptor_.bind(endpoint, ec); - if (ec) { - LOG(log_.error()) << "Failed to bind to endpoint: " << endpoint << ". message: " << ec.message(); - throw std::runtime_error( - fmt::format("Failed to bind to endpoint: {}:{}", endpoint.address().to_string(), endpoint.port()) - ); - } - - acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec); - if (ec) { - LOG(log_.error()) << "Failed to listen at endpoint: " << endpoint << ". message: " << ec.message(); - throw std::runtime_error( - fmt::format("Failed to listen at endpoint: {}:{}", endpoint.address().to_string(), endpoint.port()) - ); - } - } - - /** @brief Start accepting incoming connections. */ + /** + * @brief Set handler for GET requests. + * @note This method can't be called after run() is called. + * + * @param target The target of the request. + * @param handler The handler to set. + */ void - run() - { - doAccept(); - } + onGet(std::string const& target, MessageHandler handler); + + /** + * @brief Set handler for POST requests. + * @note This method can't be called after run() is called. + * + * @param target The target of the request. + * @param handler The handler to set. + */ + void + onPost(std::string const& target, MessageHandler handler); + + /** + * @brief Set handler for WebSocket requests. + * @note This method can't be called after run() is called. + * + * @param handler The handler to set. + */ + void + onWs(MessageHandler handler); + + /** + * @brief Run the server. + * + * @return std::nullopt if the server started successfully, otherwise an error message. + */ + std::optional + run(); + + /** + * @brief Stop the server. This method will asynchronously sleep unless all the users are disconnected. + * @note Stopping the server cause graceful shutdown of all connections. And rejecting new connections. + * + * @param yield The coroutine context. + */ + void + stop(boost::asio::yield_context yield); private: void - doAccept() - { - acceptor_.async_accept( - boost::asio::make_strand(ioc_.get()), - boost::beast::bind_front_handler(&Server::onAccept, shared_from_this()) - ); - } - - void - onAccept(boost::beast::error_code ec, tcp::socket socket) - { - if (!ec) { - auto ctxRef = - ctx_ ? std::optional>{ctx_.value()} : std::nullopt; - - std::make_shared>( - std::move(socket), - ctxRef, - std::cref(tagFactory_), - dosGuard_, - handler_, - adminVerification_, - maxWsSendingQueueSize_ - ) - ->run(); - } - - doAccept(); - } + handleConnection(boost::asio::ip::tcp::socket socket, boost::asio::yield_context yield); }; -/** @brief The final type of the HttpServer used by Clio. */ -template -using HttpServer = Server; - /** - * @brief A factory function that spawns a ready to use HTTP server. + * @brief Create a new Server. * - * @tparam HandlerType The type of handler to process the request - * @param config The config to create server - * @param ioc The server will run under this io_context - * @param dosGuard The dos guard to protect the server - * @param handler The handler to process the request - * @return The server instance + * @param config The configuration. + * @param onConnectCheck The check to perform on each client connection. + * @param onDisconnectHook The hook to call when client disconnects. + * @param context The boost::asio::io_context to use. + * + * @return The Server or an error message. */ -template -static std::shared_ptr> -makeHttpServer( +std::expected +makeServer( util::config::ClioConfigDefinition const& config, - boost::asio::io_context& ioc, - dosguard::DOSGuardInterface& dosGuard, - std::shared_ptr const& handler -) -{ - static util::Logger const log{"WebServer"}; // NOLINT(readability-identifier-naming) - - auto expectedSslContext = ng::impl::makeServerSslContext(config); - if (not expectedSslContext) { - LOG(log.error()) << "Failed to create SSL context: " << expectedSslContext.error(); - return nullptr; - } - - auto const serverConfig = config.getObject("server"); - auto const address = boost::asio::ip::make_address(serverConfig.get("ip")); - auto const port = serverConfig.get("port"); - - auto expectedAdminVerification = makeAdminVerificationStrategy(config); - if (not expectedAdminVerification.has_value()) { - LOG(log.error()) << expectedAdminVerification.error(); - throw std::logic_error{expectedAdminVerification.error()}; - } - - // If the transactions number is 200 per ledger, A client which subscribes everything will send 400+ feeds for - // each ledger. we allow user delay 3 ledgers by default - auto const maxWsSendingQueueSize = serverConfig.get("ws_max_sending_queue_size"); - - auto server = std::make_shared>( - ioc, - std::move(expectedSslContext).value(), - boost::asio::ip::tcp::endpoint{address, port}, - util::TagDecoratorFactory(config), - dosGuard, - handler, - std::move(expectedAdminVerification).value(), - maxWsSendingQueueSize - ); - - server->run(); - return server; -} + Server::OnConnectCheck onConnectCheck, + Server::OnDisconnectHook onDisconnectHook, + boost::asio::io_context& context +); } // namespace web diff --git a/src/web/SslHttpSession.hpp b/src/web/SslHttpSession.hpp deleted file mode 100644 index 43ee85e03..000000000 --- a/src/web/SslHttpSession.hpp +++ /dev/null @@ -1,188 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of clio: https://github.com/XRPLF/clio - Copyright (c) 2023, the clio developers. - - Permission to use, copy, modify, and distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#pragma once - -#include "util/Taggable.hpp" -#include "web/AdminVerificationStrategy.hpp" -#include "web/SslWsSession.hpp" -#include "web/dosguard/DOSGuardInterface.hpp" -#include "web/impl/HttpBase.hpp" -#include "web/interface/Concepts.hpp" -#include "web/interface/ConnectionBase.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace web { - -using tcp = boost::asio::ip::tcp; - -/** - * @brief Represents a HTTPS connection established by a client. - * - * It will handle the upgrade to secure websocket, pass the ownership of the socket to the upgrade session. - * Otherwise, it will pass control to the base class. - * - * @tparam HandlerType The type of the server handler to use - */ -template -class SslHttpSession : public impl::HttpBase, - public std::enable_shared_from_this> { - boost::beast::ssl_stream stream_; - std::reference_wrapper tagFactory_; - std::uint32_t maxWsSendingQueueSize_; - -public: - /** - * @brief Create a new SSL session. - * - * @param socket The socket. Ownership is transferred to HttpSession - * @param ip Client's IP address - * @param adminVerification The admin verification strategy to use - * @param ctx The SSL context - * @param tagFactory A factory that is used to generate tags to track requests and sessions - * @param dosGuard The denial of service guard to use - * @param handler The server handler to use - * @param buffer Buffer with initial data received from the peer - * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket - */ - explicit SslHttpSession( - tcp::socket&& socket, - std::string const& ip, - std::shared_ptr const& adminVerification, - boost::asio::ssl::context& ctx, - std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, - std::shared_ptr const& handler, - boost::beast::flat_buffer buffer, - std::uint32_t maxWsSendingQueueSize - ) - : impl::HttpBase( - ip, - tagFactory, - adminVerification, - dosGuard, - handler, - std::move(buffer) - ) - , stream_(std::move(socket), ctx) - , tagFactory_(tagFactory) - , maxWsSendingQueueSize_(maxWsSendingQueueSize) - { - } - - ~SslHttpSession() override = default; - - /** @return The SSL stream. */ - boost::beast::ssl_stream& - stream() - { - return stream_; - } - - /** @brief Initiates the handshake. */ - void - run() - { - auto self = this->shared_from_this(); - boost::asio::dispatch(stream_.get_executor(), [self]() { - // Set the timeout. - boost::beast::get_lowest_layer(self->stream()).expires_after(std::chrono::seconds(30)); - - // Perform the SSL handshake - // Note, this is the buffered version of the handshake. - self->stream_.async_handshake( - boost::asio::ssl::stream_base::server, - self->buffer_.data(), - boost::beast::bind_front_handler(&SslHttpSession::onHandshake, self) - ); - }); - } - - /** - * @brief Handles the handshake. - * - * @param ec Error code if any - * @param bytesUsed The total amount of data read from the stream - */ - void - onHandshake(boost::beast::error_code ec, std::size_t bytesUsed) - { - if (ec) - return this->httpFail(ec, "handshake"); - - this->buffer_.consume(bytesUsed); - this->doRead(); - } - - /** @brief Closes the underlying connection. */ - void - doClose() - { - boost::beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); - stream_.async_shutdown(boost::beast::bind_front_handler(&SslHttpSession::onShutdown, this->shared_from_this())); - } - - /** - * @brief Handles a connection shutdown. - * - * @param ec Error code if any - */ - void - onShutdown(boost::beast::error_code ec) - { - if (ec) - return this->httpFail(ec, "shutdown"); - // At this point the connection is closed gracefully - } - - /** @brief Upgrades connection to secure websocket. */ - void - upgrade() - { - std::make_shared>( - std::move(stream_), - this->clientIp, - tagFactory_, - this->dosGuard_, - this->handler_, - std::move(this->buffer_), - std::move(this->req_), - ConnectionBase::isAdmin(), - maxWsSendingQueueSize_ - ) - ->run(); - } -}; -} // namespace web diff --git a/src/web/SslWsSession.hpp b/src/web/SslWsSession.hpp deleted file mode 100644 index d5b317db3..000000000 --- a/src/web/SslWsSession.hpp +++ /dev/null @@ -1,208 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of clio: https://github.com/XRPLF/clio - Copyright (c) 2023, the clio developers. - - Permission to use, copy, modify, and distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#pragma once - -#include "util/Taggable.hpp" -#include "web/dosguard/DOSGuardInterface.hpp" -#include "web/impl/WsBase.hpp" -#include "web/interface/ConnectionBase.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace web { - -/** - * @brief Represents a secure websocket session. - * - * Majority of the operations are handled by the base class. - */ -template -class SslWsSession : public impl::WsBase { - using StreamType = boost::beast::websocket::stream>; - StreamType ws_; - -public: - /** - * @brief Create a new non-secure websocket session. - * - * @param stream The SSL stream. Ownership is transferred - * @param ip Client's IP address - * @param tagFactory A factory that is used to generate tags to track requests and sessions - * @param dosGuard The denial of service guard to use - * @param handler The server handler to use - * @param buffer Buffer with initial data received from the peer - * @param isAdmin Whether the connection has admin privileges - * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket - */ - explicit SslWsSession( - boost::beast::ssl_stream&& stream, - std::string ip, - std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, - std::shared_ptr const& handler, - boost::beast::flat_buffer&& buffer, - bool isAdmin, - std::uint32_t maxWsSendingQueueSize - ) - : impl::WsBase( - ip, - tagFactory, - dosGuard, - handler, - std::move(buffer), - maxWsSendingQueueSize - ) - , ws_(std::move(stream)) - { - ConnectionBase::isAdmin_ = isAdmin; // NOLINT(cppcoreguidelines-prefer-member-initializer) - } - - /** @return The secure websocket stream. */ - StreamType& - ws() - { - return ws_; - } -}; - -/** - * @brief The HTTPS upgrader class, upgrade from an HTTPS session to a secure websocket session. - * - * Pass the stream to the session class after upgrade. - */ -template -class SslWsUpgrader : public std::enable_shared_from_this> { - using std::enable_shared_from_this>::shared_from_this; - - boost::beast::ssl_stream https_; - boost::optional> parser_; - boost::beast::flat_buffer buffer_; - std::string ip_; - std::reference_wrapper tagFactory_; - std::reference_wrapper dosGuard_; - std::shared_ptr const handler_; - http::request req_; - bool isAdmin_; - std::uint32_t maxWsSendingQueueSize_; - -public: - /** - * @brief Create a new upgrader to secure websocket. - * - * @param stream The SSL stream. Ownership is transferred - * @param ip Client's IP address - * @param tagFactory A factory that is used to generate tags to track requests and sessions - * @param dosGuard The denial of service guard to use - * @param handler The server handler to use - * @param buffer Buffer with initial data received from the peer. Ownership is transferred - * @param request The request. Ownership is transferred - * @param isAdmin Whether the connection has admin privileges - * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket - */ - SslWsUpgrader( - boost::beast::ssl_stream stream, - std::string ip, - std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, - std::shared_ptr handler, - boost::beast::flat_buffer&& buffer, - http::request request, - bool isAdmin, - std::uint32_t maxWsSendingQueueSize - ) - : https_(std::move(stream)) - , buffer_(std::move(buffer)) - , ip_(std::move(ip)) - , tagFactory_(tagFactory) - , dosGuard_(dosGuard) - , handler_(std::move(handler)) - , req_(std::move(request)) - , isAdmin_(isAdmin) - , maxWsSendingQueueSize_(maxWsSendingQueueSize) - { - } - - ~SslWsUpgrader() = default; - - /** @brief Initiate the upgrade. */ - void - run() - { - boost::beast::get_lowest_layer(https_).expires_after(std::chrono::seconds(30)); - - boost::asio::dispatch( - https_.get_executor(), - boost::beast::bind_front_handler(&SslWsUpgrader::doUpgrade, shared_from_this()) - ); - } - -private: - void - doUpgrade() - { - parser_.emplace(); - - // Apply a reasonable limit to the allowed size of the body in bytes to prevent abuse. - static constexpr auto kMAX_BODY_SIZE = 10000; - parser_->body_limit(kMAX_BODY_SIZE); - - boost::beast::get_lowest_layer(https_).expires_after(std::chrono::seconds(30)); - onUpgrade(); - } - - void - onUpgrade() - { - if (!boost::beast::websocket::is_upgrade(req_)) - return; - - // Disable the timeout. The websocket::stream uses its own timeout settings. - boost::beast::get_lowest_layer(https_).expires_never(); - - std::make_shared>( - std::move(https_), - ip_, - tagFactory_, - dosGuard_, - handler_, - std::move(buffer_), - isAdmin_, - maxWsSendingQueueSize_ - ) - ->run(std::move(req_)); - } -}; -} // namespace web diff --git a/src/web/SubscriptionContext.cpp b/src/web/SubscriptionContext.cpp index d831d1a5f..12dc16762 100644 --- a/src/web/SubscriptionContext.cpp +++ b/src/web/SubscriptionContext.cpp @@ -21,10 +21,14 @@ #include "util/Taggable.hpp" #include "web/SubscriptionContextInterface.hpp" -#include "web/interface/ConnectionBase.hpp" +#include +#include + +#include #include #include +#include #include #include @@ -32,22 +36,39 @@ namespace web { SubscriptionContext::SubscriptionContext( util::TagDecoratorFactory const& factory, - std::shared_ptr connection + impl::WsConnectionBase& connection, + std::optional maxSendQueueSize, + boost::asio::yield_context yield, + ErrorHandler errorHandler ) - : SubscriptionContextInterface{factory}, connection_{connection} + : web::SubscriptionContextInterface(factory) + , connection_(connection) + , maxSendQueueSize_(maxSendQueueSize) + , tasksGroup_(yield) + , yield_(yield) + , errorHandler_(std::move(errorHandler)) { } -SubscriptionContext::~SubscriptionContext() -{ - onDisconnect_(this); -} - void SubscriptionContext::send(std::shared_ptr message) { - if (auto connection = connection_.lock(); connection != nullptr) - connection->send(std::move(message)); + if (disconnected_) + return; + + if (maxSendQueueSize_.has_value() and tasksGroup_.size() >= *maxSendQueueSize_) { + tasksGroup_.spawn(yield_, [this](boost::asio::yield_context innerYield) { + connection_.get().close(innerYield); + }); + disconnected_ = true; + return; + } + + tasksGroup_.spawn(yield_, [this, message = std::move(message)](boost::asio::yield_context innerYield) { + auto const maybeError = connection_.get().sendBuffer(boost::asio::buffer(*message), innerYield); + if (maybeError.has_value() and errorHandler_(*maybeError, connection_)) + connection_.get().close(innerYield); + }); } void @@ -59,13 +80,21 @@ SubscriptionContext::onDisconnect(OnDisconnectSlot const& slot) void SubscriptionContext::setApiSubversion(uint32_t value) { - apiSubVersion_ = value; + apiSubversion_ = value; } uint32_t SubscriptionContext::apiSubversion() const { - return apiSubVersion_; + return apiSubversion_; +} + +void +SubscriptionContext::disconnect(boost::asio::yield_context yield) +{ + onDisconnect_(this); + disconnected_ = true; + tasksGroup_.asyncWait(yield); } } // namespace web diff --git a/src/web/SubscriptionContext.hpp b/src/web/SubscriptionContext.hpp index 2c64fa43d..863c9f681 100644 --- a/src/web/SubscriptionContext.hpp +++ b/src/web/SubscriptionContext.hpp @@ -19,32 +19,55 @@ #pragma once +#include "util/CoroutineGroup.hpp" #include "util/Taggable.hpp" +#include "web/Connection.hpp" +#include "web/Error.hpp" #include "web/SubscriptionContextInterface.hpp" -#include "web/interface/Concepts.hpp" -#include "web/interface/ConnectionBase.hpp" +#include "web/impl/WsConnection.hpp" +#include +#include #include #include +#include #include +#include #include +#include #include namespace web { /** - * @brief A context of a WsBase connection for subscriptions. + * @brief Implementation of SubscriptionContextInterface. + * @note This class is designed to be used with SubscriptionManager. The class is safe to use from multiple threads. + * The method disconnect() must be called before the object is destroyed. */ -class SubscriptionContext : public SubscriptionContextInterface { - std::weak_ptr connection_; +class SubscriptionContext : public web::SubscriptionContextInterface { +public: + /** + * @brief Error handler definition. Error handler returns true if connection should be closed false otherwise. + */ + using ErrorHandler = std::function; + +private: + std::reference_wrapper connection_; + std::optional maxSendQueueSize_; + util::CoroutineGroup tasksGroup_; + boost::asio::yield_context yield_; + ErrorHandler errorHandler_; + boost::signals2::signal onDisconnect_; + std::atomic_bool disconnected_{false}; + /** * @brief The API version of the web stream client. * This is used to track the api version of this connection, which mainly is used by subscription. It is different * from the api version in Context, which is only used for the current request. */ - std::atomic_uint32_t apiSubVersion_ = 0; + std::atomic_uint32_t apiSubversion_ = 0u; public: /** @@ -52,17 +75,21 @@ public: * * @param factory The tag decorator factory to use to init taggable. * @param connection The connection for which the context is created. + * @param maxSendQueueSize The maximum size of the send queue. If the queue is full, the connection will be closed. + * @param yield The yield context to spawn sending coroutines. + * @param errorHandler The error handler. */ - SubscriptionContext(util::TagDecoratorFactory const& factory, std::shared_ptr connection); - - /** - * @brief Destroy the Subscription Context object - */ - ~SubscriptionContext() override; + SubscriptionContext( + util::TagDecoratorFactory const& factory, + impl::WsConnectionBase& connection, + std::optional maxSendQueueSize, + boost::asio::yield_context yield, + ErrorHandler errorHandler + ); /** * @brief Send message to the client - * @note This method will not do anything if the related connection got disconnected. + * @note This method does nothing after disconnected() was called. * * @param message The message to send. */ @@ -91,6 +118,15 @@ public: */ uint32_t apiSubversion() const override; + + /** + * @brief Notify the context that related connection is disconnected and wait for all the task to complete. + * @note This method must be called before the object is destroyed. + * + * @param yield The yield context to wait for all the tasks to complete. + */ + void + disconnect(boost::asio::yield_context yield); }; } // namespace web diff --git a/src/web/ng/impl/Concepts.hpp b/src/web/impl/Concepts.hpp similarity index 96% rename from src/web/ng/impl/Concepts.hpp rename to src/web/impl/Concepts.hpp index 7c0985d4a..5b27189f6 100644 --- a/src/web/ng/impl/Concepts.hpp +++ b/src/web/impl/Concepts.hpp @@ -24,7 +24,7 @@ #include -namespace web::ng::impl { +namespace web::impl { template concept IsTcpStream = std::is_same_v, boost::beast::tcp_stream>; @@ -32,4 +32,4 @@ concept IsTcpStream = std::is_same_v, boost::beast::tcp_stream>; template concept IsSslTcpStream = std::is_same_v, boost::asio::ssl::stream>; -} // namespace web::ng::impl +} // namespace web::impl diff --git a/src/web/ng/impl/ConnectionHandler.cpp b/src/web/impl/ConnectionHandler.cpp similarity index 97% rename from src/web/ng/impl/ConnectionHandler.cpp rename to src/web/impl/ConnectionHandler.cpp index 838c4bfb5..f6e08e2fd 100644 --- a/src/web/ng/impl/ConnectionHandler.cpp +++ b/src/web/impl/ConnectionHandler.cpp @@ -17,20 +17,20 @@ */ //============================================================================== -#include "web/ng/impl/ConnectionHandler.hpp" +#include "web/impl/ConnectionHandler.hpp" #include "util/Assert.hpp" #include "util/CoroutineGroup.hpp" #include "util/Taggable.hpp" #include "util/log/Logger.hpp" +#include "web/Connection.hpp" +#include "web/Error.hpp" +#include "web/MessageHandler.hpp" +#include "web/ProcessingPolicy.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" +#include "web/SubscriptionContext.hpp" #include "web/SubscriptionContextInterface.hpp" -#include "web/ng/Connection.hpp" -#include "web/ng/Error.hpp" -#include "web/ng/MessageHandler.hpp" -#include "web/ng/ProcessingPolicy.hpp" -#include "web/ng/Request.hpp" -#include "web/ng/Response.hpp" -#include "web/ng/SubscriptionContext.hpp" #include #include @@ -47,7 +47,7 @@ #include #include -namespace web::ng::impl { +namespace web::impl { namespace { @@ -387,4 +387,4 @@ ConnectionHandler::handleRequest( } } -} // namespace web::ng::impl +} // namespace web::impl diff --git a/src/web/ng/impl/ConnectionHandler.hpp b/src/web/impl/ConnectionHandler.hpp similarity index 95% rename from src/web/ng/impl/ConnectionHandler.hpp rename to src/web/impl/ConnectionHandler.hpp index e21b07bdd..1c8f492dd 100644 --- a/src/web/ng/impl/ConnectionHandler.hpp +++ b/src/web/impl/ConnectionHandler.hpp @@ -26,13 +26,13 @@ #include "util/prometheus/Gauge.hpp" #include "util/prometheus/Label.hpp" #include "util/prometheus/Prometheus.hpp" +#include "web/Connection.hpp" +#include "web/Error.hpp" +#include "web/MessageHandler.hpp" +#include "web/ProcessingPolicy.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" #include "web/SubscriptionContextInterface.hpp" -#include "web/ng/Connection.hpp" -#include "web/ng/Error.hpp" -#include "web/ng/MessageHandler.hpp" -#include "web/ng/ProcessingPolicy.hpp" -#include "web/ng/Request.hpp" -#include "web/ng/Response.hpp" #include #include @@ -47,7 +47,7 @@ #include #include -namespace web::ng::impl { +namespace web::impl { class ConnectionHandler { public: @@ -161,4 +161,4 @@ private: ); }; -} // namespace web::ng::impl +} // namespace web::impl diff --git a/src/web/ng/impl/ErrorHandling.cpp b/src/web/impl/ErrorHandling.cpp similarity index 97% rename from src/web/ng/impl/ErrorHandling.cpp rename to src/web/impl/ErrorHandling.cpp index 6e9a0540f..785d98d74 100644 --- a/src/web/ng/impl/ErrorHandling.cpp +++ b/src/web/impl/ErrorHandling.cpp @@ -17,13 +17,13 @@ */ //============================================================================== -#include "web/ng/impl/ErrorHandling.hpp" +#include "web/impl/ErrorHandling.hpp" #include "rpc/Errors.hpp" #include "rpc/JS.hpp" #include "util/Assert.hpp" -#include "web/ng/Request.hpp" -#include "web/ng/Response.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" #include #include @@ -37,7 +37,7 @@ namespace http = boost::beast::http; -namespace web::ng::impl { +namespace web::impl { namespace { @@ -161,4 +161,4 @@ ErrorHelper::composeError(rpc::RippledError error) const return composeErrorImpl(error, rawRequest_, request_); } -} // namespace web::ng::impl +} // namespace web::impl diff --git a/src/web/impl/ErrorHandling.hpp b/src/web/impl/ErrorHandling.hpp index d200810c6..1e5c650c6 100644 --- a/src/web/impl/ErrorHandling.hpp +++ b/src/web/impl/ErrorHandling.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ /* This file is part of clio: https://github.com/XRPLF/clio - Copyright (c) 2023, the clio developers. + Copyright (c) 2024, the clio developers. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -20,9 +20,8 @@ #pragma once #include "rpc/Errors.hpp" -#include "rpc/JS.hpp" -#include "util/Assert.hpp" -#include "web/interface/ConnectionBase.hpp" +#include "web/Request.hpp" +#include "web/Response.hpp" #include #include @@ -31,11 +30,8 @@ #include #include -#include +#include #include -#include -#include -#include namespace web::impl { @@ -43,137 +39,76 @@ namespace web::impl { * @brief A helper that attempts to match rippled reporting mode HTTP errors as close as possible. */ class ErrorHelper { - std::shared_ptr connection_; + std::reference_wrapper rawRequest_; std::optional request_; public: - ErrorHelper( - std::shared_ptr const& connection, - std::optional request = std::nullopt - ) - : connection_{connection}, request_{std::move(request)} - { - } + /** + * @brief Construct a new Error Helper object + * + * @param rawRequest The request that caused the error. + * @param request The parsed request that caused the error. + */ + ErrorHelper(Request const& rawRequest, std::optional request = std::nullopt); - void - sendError(rpc::Status const& err) const - { - if (connection_->upgraded) { - connection_->send(boost::json::serialize(composeError(err))); - } else { - // Note: a collection of crutches to match rippled output follows - if (auto const clioCode = std::get_if(&err.code)) { - switch (*clioCode) { - case rpc::ClioError::RpcInvalidApiVersion: - connection_->send( - std::string{rpc::getErrorInfo(*clioCode).error}, boost::beast::http::status::bad_request - ); - break; - case rpc::ClioError::RpcCommandIsMissing: - connection_->send("Null method", boost::beast::http::status::bad_request); - break; - case rpc::ClioError::RpcCommandIsEmpty: - connection_->send("method is empty", boost::beast::http::status::bad_request); - break; - case rpc::ClioError::RpcCommandNotString: - connection_->send("method is not string", boost::beast::http::status::bad_request); - break; - case rpc::ClioError::RpcParamsUnparsable: - connection_->send("params unparsable", boost::beast::http::status::bad_request); - break; + /** + * @brief Make an error response from a status. + * + * @param err The status to make an error response from. + * @return + */ + [[nodiscard]] Response + makeError(rpc::Status const& err) const; - // others are not applicable but we want a compilation error next time we add one - case rpc::ClioError::RpcUnknownOption: - case rpc::ClioError::RpcMalformedCurrency: - case rpc::ClioError::RpcMalformedRequest: - case rpc::ClioError::RpcMalformedOwner: - case rpc::ClioError::RpcMalformedAddress: - case rpc::ClioError::RpcFieldNotFoundTransaction: - case rpc::ClioError::RpcMalformedOracleDocumentId: - case rpc::ClioError::RpcMalformedAuthorizedCredentials: - case rpc::ClioError::EtlConnectionError: - case rpc::ClioError::EtlRequestError: - case rpc::ClioError::EtlRequestTimeout: - case rpc::ClioError::EtlInvalidResponse: - ASSERT( - false, "Unknown rpc error code {}", static_cast(*clioCode) - ); // this should never happen - break; - } - } else { - connection_->send(boost::json::serialize(composeError(err)), boost::beast::http::status::bad_request); - } - } - } + /** + * @brief Make an internal error response. + * + * @return A response with an internal error. + */ + [[nodiscard]] Response + makeInternalError() const; - void - sendInternalError() const - { - connection_->send( - boost::json::serialize(composeError(rpc::RippledError::rpcINTERNAL)), - boost::beast::http::status::internal_server_error - ); - } + /** + * @brief Make a response for when the server is not ready. + * + * @return A response with a not ready error. + */ + [[nodiscard]] Response + makeNotReadyError() const; - void - sendNotReadyError() const - { - connection_->send( - boost::json::serialize(composeError(rpc::RippledError::rpcNOT_READY)), boost::beast::http::status::ok - ); - } + /** + * @brief Make a response for when the server is too busy. + * + * @return A response with a too busy error. + */ + [[nodiscard]] Response + makeTooBusyError() const; - void - sendTooBusyError() const - { - if (connection_->upgraded) { - connection_->send( - boost::json::serialize(rpc::makeError(rpc::RippledError::rpcTOO_BUSY)), boost::beast::http::status::ok - ); - } else { - connection_->send( - boost::json::serialize(rpc::makeError(rpc::RippledError::rpcTOO_BUSY)), - boost::beast::http::status::service_unavailable - ); - } - } + /** + * @brief Make a response when json parsing fails. + * + * @return A response with a json parsing error. + */ + [[nodiscard]] Response + makeJsonParsingError() const; - void - sendJsonParsingError() const - { - if (connection_->upgraded) { - connection_->send(boost::json::serialize(rpc::makeError(rpc::RippledError::rpcBAD_SYNTAX))); - } else { - connection_->send( - fmt::format("Unable to parse JSON from the request"), boost::beast::http::status::bad_request - ); - } - } + /** + * @brief Compose an error into json object from a status. + * + * @param error The status to compose into a json object. + * @return The composed json object. + */ + [[nodiscard]] boost::json::object + composeError(rpc::Status const& error) const; - boost::json::object - composeError(auto const& error) const - { - auto e = rpc::makeError(error); - - if (request_) { - auto const appendFieldIfExist = [&](auto const& field) { - if (request_->contains(field) and not request_->at(field).is_null()) - e[field] = request_->at(field); - }; - - appendFieldIfExist(JS(id)); - - if (connection_->upgraded) - appendFieldIfExist(JS(api_version)); - - e[JS(request)] = request_.value(); - } - - if (connection_->upgraded) { - return e; - } - return {{JS(result), e}}; - } + /** + * @brief Compose an error into json object from a rippled error. + * + * @param error The rippled error to compose into a json object. + * @return The composed json object. + */ + [[nodiscard]] boost::json::object + composeError(rpc::RippledError error) const; }; } // namespace web::impl diff --git a/src/web/impl/HttpBase.hpp b/src/web/impl/HttpBase.hpp deleted file mode 100644 index 5f3fa26cd..000000000 --- a/src/web/impl/HttpBase.hpp +++ /dev/null @@ -1,327 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of clio: https://github.com/XRPLF/clio - Copyright (c) 2023, the clio developers. - - Permission to use, copy, modify, and distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#pragma once - -#include "rpc/Errors.hpp" -#include "util/Assert.hpp" -#include "util/Taggable.hpp" -#include "util/build/Build.hpp" -#include "util/log/Logger.hpp" -#include "util/prometheus/Http.hpp" -#include "web/AdminVerificationStrategy.hpp" -#include "web/SubscriptionContextInterface.hpp" -#include "web/dosguard/DOSGuardInterface.hpp" -#include "web/interface/Concepts.hpp" -#include "web/interface/ConnectionBase.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace web::impl { - -static auto constexpr kHEALTH_CHECK_HTML = R"html( - - - Test page for Clio -

Clio Test

This page shows Clio http(s) connectivity is working.

- -)html"; - -using tcp = boost::asio::ip::tcp; - -/** - * @brief This is the implementation class for http sessions - * - * @tparam Derived The derived class - * @tparam HandlerType The handler class, will be called when a request is received. - */ -template