Add book_offers RPC tests (RIPD-1283):

Migrate orderbook-test.js to cpp tests. Provide
coverage for error conditions in book_offers
RPC method.
This commit is contained in:
Mike Ellery
2016-09-22 16:08:52 -07:00
committed by Vinnie Falco
parent bb0b97f46b
commit 05e7373086
6 changed files with 568 additions and 37 deletions

View File

@@ -17,19 +17,41 @@
#include <BeastConfig.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/test/WSClient.h>
#include <ripple/test/jtx.h>
#include <ripple/beast/unit_test.h>
#include <ripple/rpc/impl/Tuning.h>
namespace ripple {
namespace test {
class Book_test : public beast::unit_test::suite
{
std::string getBookDir(jtx::Env & env, Issue const& in, Issue const& out)
{
std::string dir;
auto uBookBase = getBookBase({in, out});
auto uBookEnd = getQualityNext(uBookBase);
auto view = env.closed();
auto key = view->succ(uBookBase, uBookEnd);
if (key)
{
auto sleOfferDir = view->read(keylet::page(key.value()));
uint256 offerIndex;
unsigned int bookEntry;
cdirFirst(*view, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex, env.journal);
auto sleOffer = view->read(keylet::offer(offerIndex));
dir = to_string(sleOffer->getFieldH256(sfBookDirectory));
}
return dir;
}
public:
void
testOneSideEmptyBook()
{
testcase("One Side Empty Book");
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
@@ -50,7 +72,8 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
jv[jss::result][jss::offers].size() == 0);
BEAST_EXPECT(! jv[jss::result].isMember(jss::asks));
@@ -90,6 +113,7 @@ public:
void
testOneSideOffersInBook()
{
testcase("One Side Offers In Book");
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
@@ -119,7 +143,8 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
jv[jss::result][jss::offers].size() == 1);
BEAST_EXPECT(jv[jss::result][jss::offers][0u][jss::TakerGets] ==
@@ -163,6 +188,7 @@ public:
void
testBothSidesEmptyBook()
{
testcase("Both Sides Empty Book");
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
@@ -184,7 +210,8 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
jv[jss::result][jss::asks].size() == 0);
BEAST_EXPECT(jv[jss::result].isMember(jss::bids) &&
@@ -234,6 +261,7 @@ public:
void
testBothSidesOffersInBook()
{
testcase("Both Sides Offers In Book");
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
@@ -264,7 +292,8 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
jv[jss::result][jss::asks].size() == 1);
BEAST_EXPECT(jv[jss::result].isMember(jss::bids) &&
@@ -322,6 +351,7 @@ public:
void
testMultipleBooksOneSideEmptyBook()
{
testcase("Multiple Books, One Side Empty");
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
@@ -352,7 +382,8 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
jv[jss::result][jss::offers].size() == 0);
BEAST_EXPECT(! jv[jss::result].isMember(jss::asks));
@@ -417,6 +448,7 @@ public:
void
testMultipleBooksOneSideOffersInBook()
{
testcase("Multiple Books, One Side Offers In Book");
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
@@ -464,7 +496,8 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
jv[jss::result][jss::offers].size() == 2);
BEAST_EXPECT(jv[jss::result][jss::offers][0u][jss::TakerGets] ==
@@ -537,6 +570,7 @@ public:
void
testMultipleBooksBothSidesEmptyBook()
{
testcase("Multiple Books, Both Sides Empty Book");
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
@@ -569,7 +603,8 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
jv[jss::result][jss::asks].size() == 0);
BEAST_EXPECT(jv[jss::result].isMember(jss::bids) &&
@@ -653,6 +688,7 @@ public:
void
testMultipleBooksBothSidesOffersInBook()
{
testcase("Multiple Books, Both Sides Offers In Book");
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
@@ -703,7 +739,8 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
jv[jss::result][jss::asks].size() == 2);
BEAST_EXPECT(jv[jss::result].isMember(jss::bids) &&
@@ -800,20 +837,525 @@ public:
books)[jss::status] == "success");
}
void
testTrackOffers()
{
testcase("TrackOffers");
using namespace jtx;
Env env(*this);
Account gw {"gw"};
Account alice {"alice"};
Account bob {"bob"};
auto wsc = makeWSClient(env.app().config());
env.fund(XRP(20000), alice, bob, gw);
env.close();
auto USD = gw["USD"];
Json::Value books;
{
books[jss::books] = Json::arrayValue;
{
auto &j = books[jss::books].append(Json::objectValue);
j[jss::snapshot] = true;
j[jss::taker_gets][jss::currency] = "XRP";
j[jss::taker_pays][jss::currency] = "USD";
j[jss::taker_pays][jss::issuer] = gw.human();
}
auto jv = wsc->invoke("subscribe", books);
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
jv[jss::result][jss::offers].size() == 0);
BEAST_EXPECT(! jv[jss::result].isMember(jss::asks));
BEAST_EXPECT(! jv[jss::result].isMember(jss::bids));
}
env(rate(gw, 1.1));
env.close();
env.trust(USD(1000), alice);
env.trust(USD(1000), bob);
env(pay(gw, alice, USD(100)));
env(pay(gw, bob, USD(50)));
env(offer(alice, XRP(4000), USD(10)));
env.close();
Json::Value jvParams;
jvParams[jss::taker] = env.master.human();
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto jrr = wsc->invoke("book_offers", jvParams)[jss::result];
BEAST_EXPECT(jrr[jss::offers].isArray());
BEAST_EXPECT(jrr[jss::offers].size() == 1);
auto const jrOffer = jrr[jss::offers][0u];
BEAST_EXPECT(jrOffer[sfAccount.fieldName] == alice.human());
BEAST_EXPECT(jrOffer[sfBookDirectory.fieldName] ==
getBookDir(env, XRP, USD.issue()));
BEAST_EXPECT(jrOffer[sfBookNode.fieldName] == "0000000000000000");
BEAST_EXPECT(jrOffer[jss::Flags] == 0);
BEAST_EXPECT(jrOffer[sfLedgerEntryType.fieldName] == "Offer");
BEAST_EXPECT(jrOffer[sfOwnerNode.fieldName] == "0000000000000000");
BEAST_EXPECT(jrOffer[sfSequence.fieldName] == 3);
BEAST_EXPECT(jrOffer[jss::TakerGets] == USD(10).value().getJson(0));
BEAST_EXPECT(jrOffer[jss::TakerPays] == XRP(4000).value().getJson(0));
BEAST_EXPECT(jrOffer[jss::owner_funds] == "100");
BEAST_EXPECT(jrOffer[jss::quality] == "400000000");
BEAST_EXPECT(wsc->findMsg(5s,
[&](auto const& jv)
{
auto const& t = jv[jss::transaction];
return t[jss::TransactionType] == "OfferCreate" &&
t[jss::TakerGets] == USD(10).value().getJson(0) &&
t[jss::owner_funds] == "100" &&
t[jss::TakerPays] == XRP(4000).value().getJson(0);
}));
env(offer(bob, XRP(2000), USD(5)));
env.close();
BEAST_EXPECT(wsc->findMsg(5s,
[&](auto const& jv)
{
auto const& t = jv[jss::transaction];
return t[jss::TransactionType] == "OfferCreate" &&
t[jss::TakerGets] == USD(5).value().getJson(0) &&
t[jss::owner_funds] == "50" &&
t[jss::TakerPays] == XRP(2000).value().getJson(0);
}));
jrr = wsc->invoke("book_offers", jvParams)[jss::result];
BEAST_EXPECT(jrr[jss::offers].isArray());
BEAST_EXPECT(jrr[jss::offers].size() == 2);
auto const jrNextOffer = jrr[jss::offers][1u];
BEAST_EXPECT(jrNextOffer[sfAccount.fieldName] == bob.human());
BEAST_EXPECT(jrNextOffer[sfBookDirectory.fieldName] ==
getBookDir(env, XRP, USD.issue()));
BEAST_EXPECT(jrNextOffer[sfBookNode.fieldName] == "0000000000000000");
BEAST_EXPECT(jrNextOffer[jss::Flags] == 0);
BEAST_EXPECT(jrNextOffer[sfLedgerEntryType.fieldName] == "Offer");
BEAST_EXPECT(jrNextOffer[sfOwnerNode.fieldName] == "0000000000000000");
BEAST_EXPECT(jrNextOffer[sfSequence.fieldName] == 3);
BEAST_EXPECT(jrNextOffer[jss::TakerGets] == USD(5).value().getJson(0));
BEAST_EXPECT(jrNextOffer[jss::TakerPays] ==
XRP(2000).value().getJson(0));
BEAST_EXPECT(jrNextOffer[jss::owner_funds] == "50");
BEAST_EXPECT(jrNextOffer[jss::quality] == "400000000");
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
}
void
testBookOfferErrors()
{
testcase("BookOffersRPC Errors");
using namespace jtx;
Env env(*this);
Account gw {"gw"};
Account alice {"alice"};
env.fund(XRP(10000), alice, gw);
env.close();
auto USD = gw["USD"];
{
Json::Value jvParams;
jvParams[jss::ledger_index] = 10u;
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Missing field 'taker_pays'.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays] = Json::objectValue;
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Missing field 'taker_gets'.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays] = "not an object";
jvParams[jss::taker_gets] = Json::objectValue;
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_pays', not object.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays] = Json::objectValue;
jvParams[jss::taker_gets] = "not an object";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_gets', not object.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays] = Json::objectValue;
jvParams[jss::taker_gets] = Json::objectValue;
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Missing field 'taker_pays.currency'.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = 1;
jvParams[jss::taker_gets] = Json::objectValue;
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_pays.currency', not string.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets] = Json::objectValue;
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Missing field 'taker_gets.currency'.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = 1;
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_gets.currency', not string.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "NOT_VALID";
jvParams[jss::taker_gets][jss::currency] = "XRP";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "srcCurMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_pays.currency', bad currency.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = "NOT_VALID";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "dstAmtMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_gets.currency', bad currency.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = 1;
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_gets.issuer', not string.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_pays][jss::issuer] = 1;
jvParams[jss::taker_gets][jss::currency] = "USD";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_pays.issuer', not string.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_pays][jss::issuer] = gw.human() + "DEAD";
jvParams[jss::taker_gets][jss::currency] = "USD";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_pays.issuer', bad issuer.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_pays][jss::issuer] = toBase58(noAccount());
jvParams[jss::taker_gets][jss::currency] = "USD";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_pays.issuer', bad issuer account one.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human() + "DEAD";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_gets.issuer', bad issuer.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = toBase58(noAccount());
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_gets.issuer', bad issuer account one.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_pays][jss::issuer] = alice.human();
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Unneeded field 'taker_pays.issuer' "
"for XRP currency specification.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "USD";
jvParams[jss::taker_pays][jss::issuer] = toBase58(xrpAccount());
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_pays.issuer', expected non-XRP issuer.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker] = 1;
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker', not string.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker] = env.master.human() + "DEAD";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker'.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker] = env.master.human();
jvParams[jss::taker_pays][jss::currency] = "USD";
jvParams[jss::taker_pays][jss::issuer] = gw.human();
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "badMarket");
BEAST_EXPECT(jrr[jss::error_message] == "No such market.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker] = env.master.human();
jvParams[jss::limit] = "0"; // NOT an integer
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'limit', not unsigned integer.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "USD";
jvParams[jss::taker_pays][jss::issuer] = gw.human();
jvParams[jss::taker_gets][jss::currency] = "USD";
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker_gets.issuer', "
"expected non-XRP issuer.");
}
{
Json::Value jvParams;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "USD";
jvParams[jss::taker_pays][jss::issuer] = gw.human();
jvParams[jss::taker_gets][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto const jrr = env.rpc(
"json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed");
BEAST_EXPECT(jrr[jss::error_message] ==
"Unneeded field 'taker_gets.issuer' "
"for XRP currency specification.");
}
}
void
testBookOfferLimits(bool asAdmin)
{
testcase("BookOffer Limits");
using namespace jtx;
Env env(*this, [asAdmin]() {
auto p = std::make_unique<Config>();
setupConfigForUnitTests(*p);
if(! asAdmin)
{
(*p)["port_rpc"].set("admin","");
(*p)["port_ws"].set("admin","");
}
return p;
}());
Account gw {"gw"};
env.fund(XRP(200000), gw);
env.close();
auto USD = gw["USD"];
for(auto i = 0; i <= RPC::Tuning::bookOffers.rmax; i++)
env(offer(gw, XRP(50 + 1*i), USD(1.0 + 0.1*i)));
env.close();
Json::Value jvParams;
jvParams[jss::limit] = 1;
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_pays][jss::currency] = "XRP";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto jrr =
env.rpc("json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::offers].isArray());
BEAST_EXPECT(jrr[jss::offers].size() == (asAdmin ? 1u : 0u));
// NOTE - a marker field is not returned for this method
jvParams[jss::limit] = 0u;
jrr =
env.rpc("json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::offers].isArray());
BEAST_EXPECT(jrr[jss::offers].size() == 0u);
jvParams[jss::limit] = RPC::Tuning::bookOffers.rmax + 1;
jrr =
env.rpc("json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::offers].isArray());
BEAST_EXPECT(jrr[jss::offers].size() ==
(asAdmin ? RPC::Tuning::bookOffers.rmax + 1 : 0u));
jvParams[jss::limit] = Json::nullValue;
jrr =
env.rpc("json", "book_offers", to_string(jvParams)) [jss::result];
BEAST_EXPECT(jrr[jss::offers].isArray());
BEAST_EXPECT(jrr[jss::offers].size() ==
(asAdmin ? RPC::Tuning::bookOffers.rdefault : 0u));
}
void
run() override
{
testOneSideEmptyBook();
testOneSideOffersInBook();
testBothSidesEmptyBook();
testBothSidesOffersInBook();
testMultipleBooksOneSideEmptyBook();
testMultipleBooksOneSideOffersInBook();
testMultipleBooksBothSidesEmptyBook();
testMultipleBooksBothSidesOffersInBook();
testTrackOffers();
testBookOfferErrors();
testBookOfferLimits(true);
testBookOfferLimits(false);
}
};
@@ -821,3 +1363,4 @@ BEAST_DEFINE_TESTSUITE(Book,app,ripple);
} // test
} // ripple