From 9926d3188a8f560edc47c6d5ad98c99ada51d096 Mon Sep 17 00:00:00 2001 From: Mike Ellery Date: Wed, 20 Jul 2016 13:17:33 -0700 Subject: [PATCH] Add jtx test for account_offers RPC (RIPD-1236) Details ------- * covers existing account_offers-test.js * adds new coverage for results limiting and some negative tests (bad input) * fix bug in json value copying logic for bad seed/account error case * using new BEAST_EXPECT macros --- Builds/VisualStudio2015/RippleD.vcxproj | 4 + .../VisualStudio2015/RippleD.vcxproj.filters | 3 + src/ripple/rpc/handlers/AccountOffers.cpp | 2 +- src/ripple/rpc/tests/AccountOffers.test.cpp | 300 ++++++++++++++++++ src/ripple/unity/rpcx.cpp | 1 + 5 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 src/ripple/rpc/tests/AccountOffers.test.cpp diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj index b09046c84..8b1931dcd 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj +++ b/Builds/VisualStudio2015/RippleD.vcxproj @@ -3353,6 +3353,10 @@ True True + + True + True + True True diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters index 8cb871246..ed2b6b2f9 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters @@ -3813,6 +3813,9 @@ ripple\rpc\tests + + ripple\rpc\tests + ripple\rpc\tests diff --git a/src/ripple/rpc/handlers/AccountOffers.cpp b/src/ripple/rpc/handlers/AccountOffers.cpp index b022cb137..e3cb8548e 100644 --- a/src/ripple/rpc/handlers/AccountOffers.cpp +++ b/src/ripple/rpc/handlers/AccountOffers.cpp @@ -71,7 +71,7 @@ Json::Value doAccountOffers (RPC::Context& context) if (auto jv = RPC::accountFromString (accountID, strIdent)) { for (auto it = jv.begin (); it != jv.end (); ++it) - result[it.memberName ()] = it.key (); + result[it.memberName ()] = (*it); return result; } diff --git a/src/ripple/rpc/tests/AccountOffers.test.cpp b/src/ripple/rpc/tests/AccountOffers.test.cpp new file mode 100644 index 000000000..40bc49254 --- /dev/null +++ b/src/ripple/rpc/tests/AccountOffers.test.cpp @@ -0,0 +1,300 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2016 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 + +namespace ripple { +namespace test { + +class AccountOffers_test : public beast::unit_test::suite +{ +public: + static + std::unique_ptr + makeConfig(bool setup_admin) + { + auto p = std::make_unique(); + setupConfigForUnitTests(*p); + // the default config has admin active + // ...we remove them if setup_admin is false + if (! setup_admin) + { + (*p)["port_rpc"].set("admin",""); + (*p)["port_ws"].set("admin",""); + } + return p; + } + + // test helper + static bool checkArraySize(Json::Value const& val, unsigned int size) + { + return val.isArray() && + val.size() == size; + } + + // test helper + static bool checkMarker(Json::Value const& val) + { + return val.isMember(jss::marker) && + val[jss::marker].isString() && + val[jss::marker].asString().size() > 0; + } + + void testNonAdminMinLimit() + { + using namespace jtx; + Env env(*this, makeConfig(false)); + Account const gw ("G1"); + auto const USD_gw = gw["USD"]; + Account const bob ("bob"); + auto const USD_bob = bob["USD"]; + + env.fund(XRP(10000), gw, bob); + env.trust(USD_gw(1000), bob); + + // this is to provide some USD from gw in the + // bob account so that it can rightly + // make offers that give those USDs + env (pay (gw, bob, USD_gw (10))); + unsigned const offer_count = 12u; + for (auto i = 0u; i < offer_count; i++) + { + Json::Value jvo = offer(bob, XRP(100 + i), USD_gw(1)); + jvo[sfExpiration.fieldName] = 10000000u; + env(jvo); + } + + // make non-limited RPC call + auto const jro_nl = env.rpc("account_offers", bob.human())[jss::result][jss::offers]; + BEAST_EXPECT(checkArraySize(jro_nl, offer_count)); + + // now make a low-limit query, should get "corrected" + // to a min of 10 results with a marker set since there + // are more than 10 total + Json::Value jvParams; + jvParams[jss::account] = bob.human(); + jvParams[jss::limit] = 1u; + auto const jrr_l = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const& jro_l = jrr_l[jss::offers]; + BEAST_EXPECT(checkMarker(jrr_l)); + BEAST_EXPECT(checkArraySize(jro_l, 10u)); + } + + void testSequential(bool as_admin) + { + using namespace jtx; + Env env(*this, makeConfig(as_admin)); + Account const gw ("G1"); + auto const USD_gw = gw["USD"]; + Account const bob ("bob"); + auto const USD_bob = bob["USD"]; + + env.fund(XRP(10000), gw, bob); + env.trust(USD_gw(1000), bob); + + // this is to provide some USD from gw in the + // bob account so that it can rightly + // make offers that give those USDs + env (pay (gw, bob, USD_gw (10))); + + env(offer(bob, XRP(100), USD_bob(1))); + env(offer(bob, XRP(100), USD_gw(1))); + env(offer(bob, XRP(10), USD_gw(2))); + + // make the RPC call + auto const jro = env.rpc("account_offers", bob.human())[jss::result][jss::offers]; + if(BEAST_EXPECT(checkArraySize(jro, 3u))) + { + BEAST_EXPECT(jro[0u][jss::quality] == "100000000"); + BEAST_EXPECT(jro[0u][jss::taker_gets][jss::currency] == "USD"); + BEAST_EXPECT(jro[0u][jss::taker_gets][jss::issuer] == bob.human()); + BEAST_EXPECT(jro[0u][jss::taker_gets][jss::value] == "1"); + BEAST_EXPECT(jro[0u][jss::taker_pays] == "100000000"); + + BEAST_EXPECT(jro[1u][jss::quality] == "100000000"); + BEAST_EXPECT(jro[1u][jss::taker_gets][jss::currency] == "USD"); + BEAST_EXPECT(jro[1u][jss::taker_gets][jss::issuer] == gw.human()); + BEAST_EXPECT(jro[1u][jss::taker_gets][jss::value] == "1"); + BEAST_EXPECT(jro[1u][jss::taker_pays] == "100000000"); + + BEAST_EXPECT(jro[2u][jss::quality] == "5000000"); + BEAST_EXPECT(jro[2u][jss::taker_gets][jss::currency] == "USD"); + BEAST_EXPECT(jro[2u][jss::taker_gets][jss::issuer] == gw.human()); + BEAST_EXPECT(jro[2u][jss::taker_gets][jss::value] == "2"); + BEAST_EXPECT(jro[2u][jss::taker_pays] == "10000000"); + } + + { + // now make a limit (= 1) query for the same data + Json::Value jvParams; + jvParams[jss::account] = bob.human(); + jvParams[jss::limit] = 1u; + auto const jrr_l_1 = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const& jro_l_1 = jrr_l_1[jss::offers]; + // there is a difference in the validation of the limit param + // between admin and non-admin requests. with admin requests, the + // limit parameter is NOT subject to sane defaults, but with a + // non-admin there are pre-configured limit ranges applied. That's + // why we have different BEAST_EXPECT()s here for the two scenarios + BEAST_EXPECT(checkArraySize(jro_l_1, as_admin ? 1u : 3u)); + BEAST_EXPECT(as_admin ? checkMarker(jrr_l_1) : (! jrr_l_1.isMember(jss::marker))); + if (as_admin) + { + BEAST_EXPECT(jro[0u] == jro_l_1[0u]); + + // second item...with previous marker passed + jvParams[jss::marker] = jrr_l_1[jss::marker]; + auto const jrr_l_2 = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const& jro_l_2 = jrr_l_2[jss::offers]; + BEAST_EXPECT(checkMarker(jrr_l_2)); + BEAST_EXPECT(checkArraySize(jro_l_2, 1u)); + BEAST_EXPECT(jro[1u] == jro_l_2[0u]); + + // last item...with previous marker passed + jvParams[jss::marker] = jrr_l_2[jss::marker]; + auto const jrr_l_3 = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const& jro_l_3 = jrr_l_3[jss::offers]; + BEAST_EXPECT(! jrr_l_3.isMember(jss::marker)); + BEAST_EXPECT(checkArraySize(jro_l_3, 1u)); + BEAST_EXPECT(jro[2u] == jro_l_3[0u]); + } + else + { + BEAST_EXPECT(jro == jro_l_1); + } + } + + { + // now make a limit (= 0) query for the same data + // since we operate on the admin port, the limit + // value of 0 is not adjusted into tuned ranges for admin requests so + // we literally get 0 elements in that case. For non-admin + // requests, we get limit defaults applied thus all our results + // come back (we are below the min results limit) + Json::Value jvParams; + jvParams[jss::account] = bob.human(); + jvParams[jss::limit] = 0u; + auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + auto const& jro = jrr[jss::offers]; + BEAST_EXPECT(checkArraySize(jro, as_admin ? 0u : 3u)); + BEAST_EXPECT(as_admin ? checkMarker(jrr) : (! jrr.isMember(jss::marker))); + } + + } + + void testBadInput() + { + using namespace jtx; + Env env(*this); + Account const gw ("G1"); + auto const USD_gw = gw["USD"]; + Account const bob ("bob"); + auto const USD_bob = bob["USD"]; + + env.fund(XRP(10000), gw, bob); + env.trust(USD_gw(1000), bob); + + { + // no account field + auto const jrr = env.rpc ("account_offers")[jss::result]; + BEAST_EXPECT(jrr[jss::error] == "invalidParams"); + BEAST_EXPECT(jrr[jss::status] == "error"); + BEAST_EXPECT(jrr[jss::error_message] == "Missing field 'account'."); + } + + { + // empty string account + Json::Value jvParams; + jvParams[jss::account] = ""; + auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + BEAST_EXPECT(jrr[jss::error] == "badSeed"); + BEAST_EXPECT(jrr[jss::status] == "error"); + BEAST_EXPECT(jrr[jss::error_message] == "Disallowed seed."); + } + + { + // bogus account value + auto const jrr = env.rpc ("account_offers", "rNOT_AN_ACCOUNT")[jss::result]; + BEAST_EXPECT(jrr[jss::error] == "actNotFound"); + BEAST_EXPECT(jrr[jss::status] == "error"); + BEAST_EXPECT(jrr[jss::error_message] == "Account not found."); + } + + { + // bad limit + Json::Value jvParams; + jvParams[jss::account] = bob.human(); + jvParams[jss::limit] = "0"; // NOT an integer + auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + BEAST_EXPECT(jrr[jss::error] == "invalidParams"); + BEAST_EXPECT(jrr[jss::status] == "error"); + BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit', not unsigned integer."); + } + + { + // invalid marker + Json::Value jvParams; + jvParams[jss::account] = bob.human(); + jvParams[jss::marker] = "NOT_A_MARKER"; + auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + BEAST_EXPECT(jrr[jss::error] == "invalidParams"); + BEAST_EXPECT(jrr[jss::status] == "error"); + BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters."); + } + + { + // invalid marker - not a string + Json::Value jvParams; + jvParams[jss::account] = bob.human(); + jvParams[jss::marker] = 1; + auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + BEAST_EXPECT(jrr[jss::error] == "invalidParams"); + BEAST_EXPECT(jrr[jss::status] == "error"); + BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'marker', not string."); + } + + { + // ask for a bad ledger index + Json::Value jvParams; + jvParams[jss::account] = bob.human(); + jvParams[jss::ledger_index] = 10u; + auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result]; + BEAST_EXPECT(jrr[jss::error] == "lgrNotFound"); + BEAST_EXPECT(jrr[jss::status] == "error"); + BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound"); + } + + } + + void run() + { + testSequential(true); + testSequential(false); + testBadInput(); + testNonAdminMinLimit(); + } + +}; + +BEAST_DEFINE_TESTSUITE(AccountOffers,app,ripple); + +} +} + diff --git a/src/ripple/unity/rpcx.cpp b/src/ripple/unity/rpcx.cpp index 48e48f463..4972b9089 100644 --- a/src/ripple/unity/rpcx.cpp +++ b/src/ripple/unity/rpcx.cpp @@ -96,6 +96,7 @@ #include #include +#include #include #include #include