rippled
Loading...
Searching...
No Matches
AccountCurrencies_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/beast/unit_test.h>
4#include <xrpl/protocol/jss.h>
5
6namespace xrpl {
7
9{
10 void
12 {
13 testcase("Bad input to account_currencies");
14
15 using namespace test::jtx;
16 Env env{*this};
17
18 auto const alice = Account{"alice"};
19 env.fund(XRP(10000), alice);
20 env.close();
21
22 { // invalid ledger (hash)
23 Json::Value params;
24 params[jss::account] = Account{"bob"}.human();
25 params[jss::ledger_hash] = 1;
26 auto const result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
27 BEAST_EXPECT(result[jss::error] == "invalidParams");
28 BEAST_EXPECT(result[jss::error_message] == "Invalid field 'ledger_hash', not hex string.");
29 }
30
31 { // missing account field
32 auto const result = env.rpc("json", "account_currencies", "{}")[jss::result];
33 BEAST_EXPECT(result[jss::error] == "invalidParams");
34 BEAST_EXPECT(result[jss::error_message] == "Missing field 'account'.");
35 }
36
37 {
38 // test account non-string
39 auto testInvalidAccountParam = [&](auto const& param) {
40 Json::Value params;
41 params[jss::account] = param;
42 auto jrr = env.rpc("json", "account_currencies", to_string(params))[jss::result];
43 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
44 BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'account'.");
45 };
46
47 testInvalidAccountParam(1);
48 testInvalidAccountParam(1.1);
49 testInvalidAccountParam(true);
50 testInvalidAccountParam(Json::Value(Json::nullValue));
51 testInvalidAccountParam(Json::Value(Json::objectValue));
52 testInvalidAccountParam(Json::Value(Json::arrayValue));
53 }
54
55 {
56 // test ident non-string
57 auto testInvalidIdentParam = [&](auto const& param) {
58 Json::Value params;
59 params[jss::ident] = param;
60 auto jrr = env.rpc("json", "account_currencies", to_string(params))[jss::result];
61 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
62 BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'ident'.");
63 };
64
65 testInvalidIdentParam(1);
66 testInvalidIdentParam(1.1);
67 testInvalidIdentParam(true);
68 testInvalidIdentParam(Json::Value(Json::nullValue));
69 testInvalidIdentParam(Json::Value(Json::objectValue));
70 testInvalidIdentParam(Json::Value(Json::arrayValue));
71 }
72
73 {
74 Json::Value params;
75 params[jss::account] = "llIIOO"; // these are invalid in bitcoin alphabet
76 auto const result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
77 BEAST_EXPECT(result[jss::error] == "actMalformed");
78 BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
79 }
80
81 {
82 // Cannot use a seed as account
83 Json::Value params;
84 params[jss::account] = "Bob";
85 auto const result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
86 BEAST_EXPECT(result[jss::error] == "actMalformed");
87 BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
88 }
89
90 { // ask for nonexistent account
91 Json::Value params;
92 params[jss::account] = Account{"bob"}.human();
93 auto const result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
94 BEAST_EXPECT(result[jss::error] == "actNotFound");
95 BEAST_EXPECT(result[jss::error_message] == "Account not found.");
96 }
97 }
98
99 void
101 {
102 testcase("Basic request for account_currencies");
103
104 using namespace test::jtx;
105 Env env{*this};
106
107 auto const alice = Account{"alice"};
108 auto const gw = Account{"gateway"};
109 env.fund(XRP(10000), alice, gw);
110 char currencySuffix{'A'};
111 std::vector<std::optional<IOU>> gwCurrencies(26); // A - Z
112 std::generate(gwCurrencies.begin(), gwCurrencies.end(), [&]() {
113 auto gwc = gw[std::string("US") + currencySuffix++];
114 env(trust(alice, gwc(100)));
115 return gwc;
116 });
117 env.close();
118
119 Json::Value params;
120 params[jss::account] = alice.human();
121 auto result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
122
123 auto arrayCheck = [&result](
124 Json::StaticString const& fld, std::vector<std::optional<IOU>> const& expected) -> bool {
125 bool stat = result.isMember(fld) && result[fld].isArray() && result[fld].size() == expected.size();
126 for (size_t i = 0; stat && i < expected.size(); ++i)
127 {
128 stat &= (to_string(expected[i].value().currency) == result[fld][i].asString());
129 }
130 return stat;
131 };
132
133 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
134 BEAST_EXPECT(arrayCheck(jss::send_currencies, {}));
135
136 // now form a payment for each currency
137 for (auto const& c : gwCurrencies)
138 env(pay(gw, alice, c.value()(50)));
139
140 // send_currencies should be populated now
141 result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
142 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
143 BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
144
145 // freeze the USD trust line and verify that the receive currencies
146 // does not change
147 env(trust(alice, gw["USD"](100), tfSetFreeze));
148 result = env.rpc("account_lines", alice.human());
149 for (auto const& l : result[jss::lines])
150 BEAST_EXPECT(l[jss::freeze].asBool() == (l[jss::currency] == "USD"));
151 result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
152 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
153 BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
154 // clear the freeze
155 env(trust(alice, gw["USD"](100), tfClearFreeze));
156
157 // make a payment that exhausts the trustline from alice to gw for USA
158 env(pay(gw, alice, gw["USA"](50)));
159 // USA should now be missing from receive_currencies
160 result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
161 decltype(gwCurrencies) gwCurrenciesNoUSA(gwCurrencies.begin() + 1, gwCurrencies.end());
162 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrenciesNoUSA));
163 BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
164
165 // add trust from gw to alice and then exhaust that trust line
166 // so that send_currencies for alice will now omit USA
167 env(trust(gw, alice["USA"](100)));
168 env(pay(alice, gw, alice["USA"](200)));
169 result = env.rpc("json", "account_currencies", to_string(params))[jss::result];
170 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
171 BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrenciesNoUSA));
172 }
173
174public:
175 void
176 run() override
177 {
178 testBadInput();
179 testBasic();
180 }
181};
182
183BEAST_DEFINE_TESTSUITE(AccountCurrencies, rpc, xrpl);
184
185} // namespace xrpl
T begin(T... args)
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:147
void run() override
Runs the suite.
T end(T... args)
T generate(T... args)
@ nullValue
'null' value
Definition json_value.h:19
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:99
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:98