diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 76c5fc5fa8..9e756e7883 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -141,7 +141,6 @@ enum TEMcodes : TERUnderlyingType { temARRAY_TOO_LARGE, temBAD_TRANSFER_FEE, - temSTRING_TOO_LARGE, }; //------------------------------------------------------------------------------ diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index b309a21a2f..3d71684721 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -34,246 +33,348 @@ using namespace test::jtx; class Vault_test : public beast::unit_test::suite { void - testSequence( - Env& env, - Account const& issuer, - Account const& owner, - Account const& depositor, - Vault& vault, - PrettyAsset const& asset) - { - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - env(tx); - env.close(); - BEAST_EXPECT(env.le(keylet)); - - { - testcase("fail to deposit more than assets held"); - auto tx = vault.deposit( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(10000)}); - env(tx, ter(tecINSUFFICIENT_FUNDS)); - } - - { - testcase("deposit non-zero amount"); - auto tx = vault.deposit( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(50)}); - env(tx); - } - - { - testcase("deposit non-zero amount again"); - auto tx = vault.deposit( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(50)}); - env(tx); - } - - { - testcase("fail to delete non-empty vault"); - auto tx = vault.del({.owner = owner, .id = keylet.key}); - env(tx, ter(tecHAS_OBLIGATIONS)); - } - - { - testcase("fail to update because wrong owner"); - auto tx = vault.set({.owner = issuer, .id = keylet.key}); - env(tx, ter(tecNO_PERMISSION)); - } - - { - testcase("fail to update immutable flags"); - auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfFlags] = tfVaultPrivate; - env(tx, ter(temINVALID_FLAG)); - } - - { - testcase("fail to set maximum lower than current amount"); - auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfAssetMaximum] = asset(50).number(); - env(tx, ter(tecLIMIT_EXCEEDED)); - } - - { - testcase("set maximum higher than current amount"); - auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfAssetMaximum] = asset(200).number(); - env(tx); - } - - { - testcase("fail to deposit more than maximum"); - auto tx = vault.deposit( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(200)}); - env(tx, ter(tecLIMIT_EXCEEDED)); - } - - { - testcase("fail to withdraw more than assets held"); - auto tx = vault.withdraw( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(1000)}); - env(tx, ter(tecINSUFFICIENT_FUNDS)); - } - - { - testcase("deposit up to maximum"); - auto tx = vault.deposit( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(100)}); - env(tx); - } - - if (!asset.raw().native()) - { - testcase("fail to clawback because wrong issuer"); - auto tx = vault.clawback( - {.issuer = owner, - .id = keylet.key, - .holder = depositor, - .amount = asset(50)}); - env(tx, ter(tecNO_PERMISSION)); - } - - { - testcase("clawback"); - auto code = - asset.raw().native() ? ter(tecNO_PERMISSION) : ter(tesSUCCESS); - auto tx = vault.clawback( - {.issuer = issuer, - .id = keylet.key, - .holder = depositor, - .amount = asset(50)}); - env(tx, code); - } - - // TODO: redeem. - - { - testcase("withdraw non-zero assets"); - auto number = asset.raw().native() ? 200 : 150; - auto tx = vault.withdraw( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(number)}); - env(tx); - } - - { - testcase("fail to delete because wrong owner"); - auto tx = vault.del({.owner = issuer, .id = keylet.key}); - env(tx, ter(tecNO_PERMISSION)); - } - - { - testcase("delete empty vault"); - auto tx = vault.del({.owner = owner, .id = keylet.key}); - env(tx); - BEAST_EXPECT(!env.le(keylet)); - } - } - - TEST_CASE(Sequences) + testSequences() { using namespace test::jtx; - Env env{*this}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Account depositor{"depositor"}; - auto vault = env.vault(); - env.fund(XRP(1000), issuer, owner, depositor); - env.close(); - - SUBCASE("XRP") - { - PrettyAsset asset{xrpIssue(), 1'000'000}; - testSequence(env, issuer, owner, depositor, vault, asset); - } - - SUBCASE("IOU") - { - PrettyAsset asset = issuer["IOU"]; - env.trust(asset(1000), depositor); - env(pay(issuer, depositor, asset(1000))); + auto const testSequence = [this]( + std::string const& prefix, + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Vault& vault, + PrettyAsset const& asset) { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); env.close(); - testSequence(env, issuer, owner, depositor, vault, asset); - } + BEAST_EXPECT(env.le(keylet)); - SUBCASE("MPT") - { - MPTTester mptt{env, issuer, {.fund = false}}; - mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); - PrettyAsset asset = mptt.issuanceID(); - mptt.authorize({.account = depositor}); - env(pay(issuer, depositor, asset(1000))); - env.close(); - testSequence(env, issuer, owner, depositor, vault, asset); - } + { + testcase(prefix + " fail to deposit more than assets held"); + auto tx = vault.deposit( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(10000)}); + env(tx, ter(tecINSUFFICIENT_FUNDS)); + } + + { + testcase(prefix + " deposit non-zero amount"); + auto tx = vault.deposit( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(50)}); + env(tx); + } + + { + testcase(prefix + " deposit non-zero amount again"); + auto tx = vault.deposit( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(50)}); + env(tx); + } + + { + testcase(prefix + " fail to delete non-empty vault"); + auto tx = vault.del({.owner = owner, .id = keylet.key}); + env(tx, ter(tecHAS_OBLIGATIONS)); + } + + { + testcase(prefix + " fail to update because wrong owner"); + auto tx = vault.set({.owner = issuer, .id = keylet.key}); + env(tx, ter(tecNO_PERMISSION)); + } + + { + testcase(prefix + " fail to update immutable flags"); + auto tx = vault.set({.owner = owner, .id = keylet.key}); + tx[sfFlags] = tfVaultPrivate; + env(tx, ter(temINVALID_FLAG)); + } + + { + testcase( + prefix + " fail to set maximum lower than current amount"); + auto tx = vault.set({.owner = owner, .id = keylet.key}); + tx[sfAssetMaximum] = asset(50).number(); + env(tx, ter(tecLIMIT_EXCEEDED)); + } + + { + testcase(prefix + " set maximum higher than current amount"); + auto tx = vault.set({.owner = owner, .id = keylet.key}); + tx[sfAssetMaximum] = asset(200).number(); + env(tx); + } + + { + testcase(prefix + " fail to deposit more than maximum"); + auto tx = vault.deposit( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(200)}); + env(tx, ter(tecLIMIT_EXCEEDED)); + } + + { + testcase(prefix + " fail to withdraw more than assets held"); + auto tx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(1000)}); + env(tx, ter(tecINSUFFICIENT_FUNDS)); + } + + { + testcase(prefix + " deposit up to maximum"); + auto tx = vault.deposit( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(100)}); + env(tx); + } + + if (!asset.raw().native()) + { + testcase(prefix + " fail to clawback because wrong issuer"); + auto tx = vault.clawback( + {.issuer = owner, + .id = keylet.key, + .holder = depositor, + .amount = asset(50)}); + env(tx, ter(tecNO_PERMISSION)); + } + + { + testcase(prefix + " clawback"); + auto code = asset.raw().native() ? ter(tecNO_PERMISSION) + : ter(tesSUCCESS); + auto tx = vault.clawback( + {.issuer = issuer, + .id = keylet.key, + .holder = depositor, + .amount = asset(50)}); + env(tx, code); + } + + // TODO: redeem. + + { + testcase("withdraw non-zero assets"); + auto number = asset.raw().native() ? 200 : 150; + auto tx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(number)}); + env(tx); + } + + { + testcase("fail to delete because wrong owner"); + auto tx = vault.del({.owner = issuer, .id = keylet.key}); + env(tx, ter(tecNO_PERMISSION)); + } + + { + testcase("delete empty vault"); + auto tx = vault.del({.owner = owner, .id = keylet.key}); + env(tx); + BEAST_EXPECT(!env.le(keylet)); + } + }; + + auto testCases = + [this, &testSequence]( + std::string prefix, + std::function + setup) { + Env env{*this}; + Account issuer{"issuer"}; + Account owner{"owner"}; + Account depositor{"depositor"}; + auto vault = env.vault(); + env.fund(XRP(1000), issuer, owner, depositor); + env.close(); + + PrettyAsset asset = setup(env, issuer, depositor); + testSequence( + prefix, env, issuer, owner, depositor, vault, asset); + }; + + testCases( + "XRP", + [](Env& env, Account const& issuer, Account const& depositor) + -> PrettyAsset { return {xrpIssue(), 1'000'000}; + }); + + testCases( + "IOU", + [](Env& env, + Account const& issuer, + Account const& depositor) -> Asset { + PrettyAsset asset = issuer["IOU"]; + env.trust(asset(1000), depositor); + env(pay(issuer, depositor, asset(1000))); + env.close(); + return asset; + }); + + testCases( + "MPT", + [](Env& env, + Account const& issuer, + Account const& depositor) -> Asset { + MPTTester mptt{env, issuer, {.fund = false}}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset asset = mptt.issuanceID(); + mptt.authorize({.account = depositor}); + env(pay(issuer, depositor, asset(1000))); + env.close(); + return asset; + }); } // Test for non-asset specific behaviors. - TEST_CASE(CreateFailXRP) + void + testCreateFailXRP() { using namespace test::jtx; - Env env{*this}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Account depositor{"depositor"}; - env.fund(XRP(1000), issuer, owner, depositor); - env.close(); - auto vault = env.vault(); - Asset asset = xrpIssue(); - SUBCASE("nothing to delete") - { + auto testCase = [this](std::function test) { + Env env{*this}; + Account issuer{"issuer"}; + Account owner{"owner"}; + Account depositor{"depositor"}; + env.fund(XRP(1000), issuer, owner, depositor); + env.close(); + auto vault = env.vault(); + Asset asset = xrpIssue(); + + test(env, issuer, owner, depositor, asset, vault); + }; + + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + testcase("nothing to delete"); auto tx = vault.del({.owner = issuer, .id = keylet::skip().key}); env(tx, ter(tecOBJECT_NOT_FOUND)); - } + }); - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + testcase("transaction is good"); + env(tx); + }); - SUBCASE("insufficient fee") - { + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + testcase("insufficient fee"); env(tx, fee(env.current()->fees().base), ter(telINSUF_FEE_P)); - } + }); - SUBCASE("insufficient reserve") - { + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + testcase("insufficient reserve"); // It is possible to construct a complicated mathematical // expression for this amount, but it is sadly not easy. env(pay(owner, issuer, XRP(775))); env.close(); env(tx, ter(tecINSUFFICIENT_RESERVE)); - } + }); - SUBCASE("data too large") - { + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + testcase("empty data"); + tx[sfData] = ""; + env(tx, ter(temMALFORMED)); + }); + + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + testcase("data too large"); // A hexadecimal string of 257 bytes. tx[sfData] = std::string(514, 'A'); - env(tx, ter(temSTRING_TOO_LARGE)); - } + env(tx, ter(temMALFORMED)); + }); - SUBCASE("metadata too large") - { + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + testcase("empty metadata"); + tx[sfMPTokenMetadata] = ""; + env(tx, ter(temMALFORMED)); + }); + + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + + testcase("metadata too large"); // This metadata is for the share token. // A hexadecimal string of 1025 bytes. tx[sfMPTokenMetadata] = std::string(2050, 'B'); - env(tx, ter(temSTRING_TOO_LARGE)); - } + env(tx, ter(temMALFORMED)); + }); } - TEST_CASE(CreateFailIOU) + void + testCreateFailIOU() { using namespace test::jtx; Env env{*this}; @@ -287,16 +388,14 @@ class Vault_test : public beast::unit_test::suite auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - SUBCASE("global freeze") - { - env(fset(issuer, asfGlobalFreeze)); - env.close(); - env(tx, ter(tecFROZEN)); - env.close(); - } + env(fset(issuer, asfGlobalFreeze)); + env.close(); + env(tx, ter(tecFROZEN)); + env.close(); } - TEST_CASE(CreateFailMPT) + void + testCreateFailMPT() { using namespace test::jtx; Env env{*this}; @@ -309,43 +408,67 @@ class Vault_test : public beast::unit_test::suite MPTTester mptt{env, issuer, {.fund = false}}; - SUBCASE("cannot transfer") - { - // Locked because that is the default flag. - mptt.create(); - Asset asset = mptt.issuanceID(); - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - env(tx, ter(tecLOCKED)); - } + // Locked because that is the default flag. + mptt.create(); + Asset asset = mptt.issuanceID(); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx, ter(tecLOCKED)); } - TEST_CASE(WithMPT) + void + testWithMPT() { using namespace test::jtx; - Env env{*this}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Account depositor{"depositor"}; - env.fund(XRP(1000), issuer, owner, depositor); - env.close(); - auto vault = env.vault(); - MPTTester mptt{env, issuer, {.fund = false}}; - mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); - PrettyAsset asset = mptt.issuanceID(); - mptt.authorize({.account = depositor}); - env(pay(issuer, depositor, asset(1000))); - env.close(); + auto testCase = [this](std::function test) { + Env env{*this}; + Account issuer{"issuer"}; + Account owner{"owner"}; + Account depositor{"depositor"}; + env.fund(XRP(1000), issuer, owner, depositor); + env.close(); + auto vault = env.vault(); - SUBCASE("global lock") - { + MPTTester mptt{env, issuer, {.fund = false}}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset asset = mptt.issuanceID(); + mptt.authorize({.account = depositor}); + env(pay(issuer, depositor, asset(1000))); + env.close(); + + test(env, issuer, owner, depositor, asset, vault, mptt); + }; + + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault, + MPTTester& mptt) { + testcase("global lock"); mptt.set({.account = issuer, .flags = tfMPTLock}); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); env(tx, ter(tecLOCKED)); - } + }); - SUBCASE("deposit non-zero amount") - { + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault, + MPTTester& mptt) { + testcase("deposit non-zero amount"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); env(tx); env.close(); @@ -365,18 +488,18 @@ class Vault_test : public beast::unit_test::suite Number outstandingShares = issuance->at(sfOutstandingAmount); BEAST_EXPECT(outstandingShares > 0); BEAST_EXPECT(outstandingShares == 100); - } + }); } public: void run() override { - EXECUTE(Sequences); - EXECUTE(CreateFailXRP); - EXECUTE(CreateFailIOU); - EXECUTE(CreateFailMPT); - EXECUTE(WithMPT); + testSequences(); + testCreateFailXRP(); + testCreateFailIOU(); + testCreateFailMPT(); + testWithMPT(); } }; diff --git a/src/test/jtx/amount.h b/src/test/jtx/amount.h index 5d7cb7fac1..56e174aafc 100644 --- a/src/test/jtx/amount.h +++ b/src/test/jtx/amount.h @@ -200,7 +200,7 @@ public: operator()(T v) const { STAmount amount{asset_, v * scale_}; - return {amount, "uhh"}; + return {amount, ""}; } }; //------------------------------------------------------------------------------ diff --git a/src/test/jtx/impl/subcases.cpp b/src/test/jtx/impl/subcases.cpp deleted file mode 100644 index 92af8eba8a..0000000000 --- a/src/test/jtx/impl/subcases.cpp +++ /dev/null @@ -1,89 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2024 Ripple Labs Inc. - - Permission to use, copy, modify, and/or 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 - -#include -#include - -namespace subcases { - -thread_local Subcase* Subcase::lastCreated; - -Subcase::Subcase(Context& context, char const* name) - : context_(context), name_(name) -{ - lastCreated = this; -} - -Subcase::operator bool() const -{ - auto& _ = context_; - ++_.level; - if (_.level >= MAXIMUM_SUBCASE_DEPTH) - throw std::logic_error("maximum subcase depth exceeded"); - if (_.entered < _.level && _.skip[_.level] == _.skipped) - { - _.entered = _.level; - _.names[_.level] = name_; - _.skipped = 0; - return true; - } - ++_.skipped; - return false; -} - -Subcase::~Subcase() -{ - auto& _ = context_; - if (_.level == _.entered && _.skipped == 0) - { - // We are destroying the leaf subcase that executed on this pass. - // Didn't have time to debug this. Cannot explain what is going wrong - // with jtx. Just switch to a better test framework already. - _.suite.pass(); - // We call `suite::testcase()` here, after the subcase is finished, - // because only now do we know which subcase was the leaf, - // and we only want to print one name line for each subcase. - _.suite.testcase(_.name()); - // Let the runner know that a test executed, - // even if `BEAST_EXPECT` was never called. - _.suite.pass(); - } - if (_.skipped == 0) - { - ++_.skip[_.level]; - _.skip[_.level + 1] = 0; - } - --_.level; -} - -void -execute(beast::unit_test::suite* suite, char const* name, Supercase supercase) -{ - Context context{*suite}; - context.names[0] = name; - do - { - context.lap(); - supercase(context); - } while (context.skipped != 0); -} - -} // namespace subcases diff --git a/src/test/jtx/subcases.h b/src/test/jtx/subcases.h deleted file mode 100644 index 1ab3bf67a6..0000000000 --- a/src/test/jtx/subcases.h +++ /dev/null @@ -1,139 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2024 Ripple Labs Inc. - - Permission to use, copy, modify, and/or 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. -*/ -//============================================================================== - -#ifndef RIPPLE_TEST_JTX_SUBCASES_H_INCLUDED -#define RIPPLE_TEST_JTX_SUBCASES_H_INCLUDED - -#include - -#include -#include - -namespace subcases { - -constexpr std::size_t MAXIMUM_SUBCASE_DEPTH = 10; - -/** - * This short library implements a pattern found in doctest and Catch: - * - * TEST_CASE(testName) { - * // setup - * SUBCASE("one") { - * // actions and assertions - * } - * SUBCASE("two") { - * // actions and assertions - * } - * SUBCASE("three") { - * // actions and assertions - * } - * // assertions before teardown - * } - * - * EXECUTE(testName); - * - * In short: - * - * - Top-level test cases are declared with `TEST_CASE(name)`. - * The name must be a legal identifier. - * It will become the name of a function. - * - Subcases are declared with `SUBCASE("description")`. - * Descriptions do not need to be unique. - * - Test cases are executed with `EXECUTE(name)`, - * where `name` is the one that was passed to `TEST_CASE`. - * When executing a test case, it will loop, - * executing exactly one leaf subcase in each pass, - * until all subcases have executed. - * The top-level test case is considered a subcase too. - * - * This lets test authors easily share common setup among multiple subcases. - * Subcases can be nested up to `MAXIMUM_SUBCASE_DEPTH`. - */ - -struct Context -{ - beast::unit_test::suite& suite; - // The number of subcases to skip at each level to reach the next subcase. - std::uint8_t skip[MAXIMUM_SUBCASE_DEPTH] = {0}; - // The subcase names at each level. - char const* names[MAXIMUM_SUBCASE_DEPTH] = {""}; - // The current level. - std::uint8_t level = 0; - // The maximum depth at which we entered a subcase. - std::uint8_t entered = 0; - // The number of subcases we skipped on this or deeper levels - // since entering a subcase. - std::uint8_t skipped = 0; - - std::string - name() const - { - std::string n; - for (auto i = 0; i <= level; ++i) - { - if (i != 0) - { - n += " > "; - } - n += names[i]; - } - return n; - } - - void - lap() - { - level = 0; - entered = 0; - skipped = 0; - } -}; - -struct Subcase -{ - Context& context_; - char const* name_; - Subcase(Context& context, char const* name); - ~Subcase(); - /** Return true if we should enter this subcase. */ - operator bool() const; - thread_local static Subcase* lastCreated; -}; - -using Supercase = std::function; - -void -execute(beast::unit_test::suite* suite, char const* name, Supercase supercase); - -} // namespace subcases - -#define TEST_CASE(name) void name(subcases::Context& _09876) -#define SUBCASE(name) if (subcases::Subcase sc##__COUNTER__{_09876, name}) -#define SKIP(name) if (false) -#define EXECUTE(name) \ - subcases::execute(this, #name, [&](auto& ctx) { name(ctx); }) -// `AND_THEN` defines a subcase to contain all remaining subcases, -// without having to indent them in a nested block. -#define AND_THEN(name) \ - subcases::Subcase sc##__COUNTER__{_09876, name}; \ - if (!*subcases::Subcase::lastCreated) \ - return -#define SECTION(name_) _09876.suite.testcase(_09876.name() + " > " + name_) - -#endif diff --git a/src/xrpld/app/misc/CredentialHelpers.cpp b/src/xrpld/app/misc/CredentialHelpers.cpp index a8ec360744..f117f7f8ce 100644 --- a/src/xrpld/app/misc/CredentialHelpers.cpp +++ b/src/xrpld/app/misc/CredentialHelpers.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -193,18 +194,20 @@ authorizedDomain( uint256 domainID, AccountID const& subject) { - auto const sle = view.read(keylet::permissionedDomain(domainID)); - if (!sle || !sle->isFieldPresent(sfAcceptedCredentials)) + auto const slePD = view.read(keylet::permissionedDomain(domainID)); + if (!slePD || !slePD->isFieldPresent(sfAcceptedCredentials)) return tefINTERNAL; - for (auto const& h : sle->getFieldArray(sfAcceptedCredentials)) + for (auto const& h : slePD->getFieldArray(sfAcceptedCredentials)) { if (!h.isFieldPresent(sfIssuer) || !h.isFieldPresent(sfCredentialType)) return tefINTERNAL; auto const issuer = h.getAccountID(sfIssuer); auto const type = makeSlice(h.getFieldVL(sfCredentialType)); - if (view.exists(keylet::credential(subject, issuer, type))) + auto const sleCredential = + view.read(keylet::credential(subject, issuer, type)); + if (sleCredential && sleCredential->getFlags() & lsfAccepted) return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index 8c00acb745..b015571c63 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -42,8 +42,8 @@ VaultCreate::preflight(PreflightContext const& ctx) if (auto const data = ctx.tx[~sfData]) { - if (data->length() > maxVaultDataLength) - return temSTRING_TOO_LARGE; + if (data->empty() || data->length() > maxVaultDataLength) + return temMALFORMED; } if (auto const domain = ctx.tx[~sfDomainID]) diff --git a/src/xrpld/app/tx/detail/VaultDeposit.cpp b/src/xrpld/app/tx/detail/VaultDeposit.cpp index b4c2f86097..6dafaac1cd 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.cpp +++ b/src/xrpld/app/tx/detail/VaultDeposit.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace ripple { @@ -57,8 +58,8 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) { if (auto const domain = vault->at(~sfVaultID)) { - if (!credentials::authorizedDomain( - ctx.view, *domain, ctx.tx[sfAccount])) + if (credentials::authorizedDomain( + ctx.view, *domain, ctx.tx[sfAccount]) != tesSUCCESS) return tecNO_PERMISSION; } } diff --git a/src/xrpld/app/tx/detail/VaultSet.cpp b/src/xrpld/app/tx/detail/VaultSet.cpp index 926dec88c7..2040ace1af 100644 --- a/src/xrpld/app/tx/detail/VaultSet.cpp +++ b/src/xrpld/app/tx/detail/VaultSet.cpp @@ -39,8 +39,8 @@ VaultSet::preflight(PreflightContext const& ctx) return temINVALID_FLAG; if (auto const data = ctx.tx[~sfData]) { - if (data->length() > maxVaultDataLength) - return temSTRING_TOO_LARGE; + if (data->empty() || data->length() > maxVaultDataLength) + return temMALFORMED; } auto const domain = ctx.tx[~sfDomainID];