rippled
Loading...
Searching...
No Matches
AccountOffers_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/protocol/jss.h>
4
5namespace ripple {
6namespace test {
7
9{
10public:
11 // test helper
12 static bool
14 {
15 return val.isMember(jss::marker) && val[jss::marker].isString() &&
16 val[jss::marker].asString().size() > 0;
17 }
18
19 void
21 {
22 testcase("Non-Admin Min Limit");
23
24 using namespace jtx;
25 Env env{*this, envconfig(no_admin)};
26 Account const gw("G1");
27 auto const USD_gw = gw["USD"];
28 Account const bob("bob");
29 auto const USD_bob = bob["USD"];
30
31 env.fund(XRP(10000), gw, bob);
32 env.trust(USD_gw(1000), bob);
33
34 // this is to provide some USD from gw in the
35 // bob account so that it can rightly
36 // make offers that give those USDs
37 env(pay(gw, bob, USD_gw(10)));
38 unsigned const offer_count = 12u;
39 for (auto i = 0u; i < offer_count; i++)
40 {
41 Json::Value jvo = offer(bob, XRP(100 + i), USD_gw(1));
42 jvo[sfExpiration.fieldName] = 10000000u;
43 env(jvo);
44 }
45
46 // make non-limited RPC call
47 auto const jro_nl =
48 env.rpc("account_offers", bob.human())[jss::result][jss::offers];
49 BEAST_EXPECT(checkArraySize(jro_nl, offer_count));
50
51 // now make a low-limit query, should get "corrected"
52 // to a min of 10 results with a marker set since there
53 // are more than 10 total
54 Json::Value jvParams;
55 jvParams[jss::account] = bob.human();
56 jvParams[jss::limit] = 1u;
57 auto const jrr_l = env.rpc(
58 "json", "account_offers", jvParams.toStyledString())[jss::result];
59 auto const& jro_l = jrr_l[jss::offers];
60 BEAST_EXPECT(checkMarker(jrr_l));
61 // 9u is the expected size, since one account object is a trustline
62 BEAST_EXPECT(checkArraySize(jro_l, 9u));
63 }
64
65 void
66 testSequential(bool asAdmin)
67 {
69 std::string("Sequential - ") + (asAdmin ? "admin" : "non-admin"));
70
71 using namespace jtx;
72 Env env{*this, asAdmin ? envconfig() : envconfig(no_admin)};
73 Account const gw("G1");
74 auto const USD_gw = gw["USD"];
75 Account const bob("bob");
76 auto const USD_bob = bob["USD"];
77
78 env.fund(XRP(10000), gw, bob);
79 env.trust(USD_gw(1000), bob);
80
81 // this is to provide some USD from gw in the
82 // bob account so that it can rightly
83 // make offers that give those USDs
84 env(pay(gw, bob, USD_gw(10)));
85
86 env(offer(bob, XRP(100), USD_bob(1)));
87 env(offer(bob, XRP(200), USD_gw(2)));
88 env(offer(bob, XRP(30), USD_gw(6)));
89
90 // make the RPC call
91 auto const jroOuter =
92 env.rpc("account_offers", bob.human())[jss::result][jss::offers];
93 if (BEAST_EXPECT(checkArraySize(jroOuter, 3u)))
94 {
95 // Note that the returned offers are sorted by index, not by
96 // order of insertion or by sequence number. There is no
97 // guarantee that their order will not change in the future
98 // if the sequence numbers or the account IDs change.
99 BEAST_EXPECT(jroOuter[0u][jss::quality] == "100000000");
100 BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::currency] == "USD");
101 BEAST_EXPECT(
102 jroOuter[0u][jss::taker_gets][jss::issuer] == gw.human());
103 BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::value] == "2");
104 BEAST_EXPECT(jroOuter[0u][jss::taker_pays] == "200000000");
105
106 BEAST_EXPECT(jroOuter[1u][jss::quality] == "100000000");
107 BEAST_EXPECT(jroOuter[1u][jss::taker_gets][jss::currency] == "USD");
108 BEAST_EXPECT(
109 jroOuter[1u][jss::taker_gets][jss::issuer] == bob.human());
110 BEAST_EXPECT(jroOuter[1u][jss::taker_gets][jss::value] == "1");
111 BEAST_EXPECT(jroOuter[1u][jss::taker_pays] == "100000000");
112
113 BEAST_EXPECT(jroOuter[2u][jss::quality] == "5000000");
114 BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::currency] == "USD");
115 BEAST_EXPECT(
116 jroOuter[2u][jss::taker_gets][jss::issuer] == gw.human());
117 BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::value] == "6");
118 BEAST_EXPECT(jroOuter[2u][jss::taker_pays] == "30000000");
119 }
120
121 {
122 // now make a limit (= 1) query for the same data
123 Json::Value jvParams;
124 jvParams[jss::account] = bob.human();
125 jvParams[jss::limit] = 1u;
126 auto const jrr_l_1 = env.rpc(
127 "json",
128 "account_offers",
129 jvParams.toStyledString())[jss::result];
130 auto const& jro_l_1 = jrr_l_1[jss::offers];
131 // there is a difference in the validation of the limit param
132 // between admin and non-admin requests. with admin requests, the
133 // limit parameter is NOT subject to sane defaults, but with a
134 // non-admin there are pre-configured limit ranges applied. That's
135 // why we have different BEAST_EXPECT()s here for the two scenarios
136 BEAST_EXPECT(checkArraySize(jro_l_1, asAdmin ? 1u : 3u));
137 BEAST_EXPECT(
138 asAdmin ? checkMarker(jrr_l_1)
139 : (!jrr_l_1.isMember(jss::marker)));
140 if (asAdmin)
141 {
142 BEAST_EXPECT(jroOuter[0u] == jro_l_1[0u]);
143
144 // second item...with previous marker passed
145 jvParams[jss::marker] = jrr_l_1[jss::marker];
146 auto const jrr_l_2 = env.rpc(
147 "json",
148 "account_offers",
149 jvParams.toStyledString())[jss::result];
150 auto const& jro_l_2 = jrr_l_2[jss::offers];
151 BEAST_EXPECT(checkMarker(jrr_l_2));
152 BEAST_EXPECT(checkArraySize(jro_l_2, 1u));
153 BEAST_EXPECT(jroOuter[1u] == jro_l_2[0u]);
154
155 // last item...with previous marker passed
156 jvParams[jss::marker] = jrr_l_2[jss::marker];
157 jvParams[jss::limit] = 10u;
158 auto const jrr_l_3 = env.rpc(
159 "json",
160 "account_offers",
161 jvParams.toStyledString())[jss::result];
162 auto const& jro_l_3 = jrr_l_3[jss::offers];
163 BEAST_EXPECT(!jrr_l_3.isMember(jss::marker));
164 BEAST_EXPECT(checkArraySize(jro_l_3, 1u));
165 BEAST_EXPECT(jroOuter[2u] == jro_l_3[0u]);
166 }
167 else
168 {
169 BEAST_EXPECT(jroOuter == jro_l_1);
170 }
171 }
172
173 {
174 Json::Value jvParams;
175 jvParams[jss::account] = bob.human();
176 jvParams[jss::limit] = 0u;
177 auto const jrr = env.rpc(
178 "json",
179 "account_offers",
180 jvParams.toStyledString())[jss::result];
181 BEAST_EXPECT(jrr.isMember(jss::error_message));
182 }
183 }
184
185 void
187 {
188 testcase("Bad input");
189
190 using namespace jtx;
191 Env env(*this);
192 Account const gw("G1");
193 auto const USD_gw = gw["USD"];
194 Account const bob("bob");
195 auto const USD_bob = bob["USD"];
196
197 env.fund(XRP(10000), gw, bob);
198 env.trust(USD_gw(1000), bob);
199
200 {
201 // no account field
202 auto const jrr = env.rpc("account_offers");
203 BEAST_EXPECT(jrr[jss::error] == "badSyntax");
204 BEAST_EXPECT(jrr[jss::status] == "error");
205 BEAST_EXPECT(jrr[jss::error_message] == "Syntax error.");
206 }
207
208 {
209 // test account non-string
210 auto testInvalidAccountParam = [&](auto const& param) {
211 Json::Value params;
212 params[jss::account] = param;
213 auto jrr = env.rpc(
214 "json", "account_offers", to_string(params))[jss::result];
215 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
216 BEAST_EXPECT(
217 jrr[jss::error_message] == "Invalid field 'account'.");
218 };
219
220 testInvalidAccountParam(1);
221 testInvalidAccountParam(1.1);
222 testInvalidAccountParam(true);
223 testInvalidAccountParam(Json::Value(Json::nullValue));
224 testInvalidAccountParam(Json::Value(Json::objectValue));
225 testInvalidAccountParam(Json::Value(Json::arrayValue));
226 }
227
228 {
229 // empty string account
230 Json::Value jvParams;
231 jvParams[jss::account] = "";
232 auto const jrr = env.rpc(
233 "json",
234 "account_offers",
235 jvParams.toStyledString())[jss::result];
236 BEAST_EXPECT(jrr[jss::error] == "actMalformed");
237 BEAST_EXPECT(jrr[jss::status] == "error");
238 BEAST_EXPECT(jrr[jss::error_message] == "Account malformed.");
239 }
240
241 {
242 // bogus account value
243 auto const jrr = env.rpc(
244 "account_offers", Account("bogus").human())[jss::result];
245 BEAST_EXPECT(jrr[jss::error] == "actNotFound");
246 BEAST_EXPECT(jrr[jss::status] == "error");
247 BEAST_EXPECT(jrr[jss::error_message] == "Account not found.");
248 }
249
250 {
251 // bad limit
252 Json::Value jvParams;
253 jvParams[jss::account] = bob.human();
254 jvParams[jss::limit] = "0"; // NOT an integer
255 auto const jrr = env.rpc(
256 "json",
257 "account_offers",
258 jvParams.toStyledString())[jss::result];
259 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
260 BEAST_EXPECT(jrr[jss::status] == "error");
261 BEAST_EXPECT(
262 jrr[jss::error_message] ==
263 "Invalid field 'limit', not unsigned integer.");
264 }
265
266 {
267 // invalid marker
268 Json::Value jvParams;
269 jvParams[jss::account] = bob.human();
270 jvParams[jss::marker] = "NOT_A_MARKER";
271 auto const jrr = env.rpc(
272 "json",
273 "account_offers",
274 jvParams.toStyledString())[jss::result];
275 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
276 BEAST_EXPECT(jrr[jss::status] == "error");
277 BEAST_EXPECTS(
278 jrr[jss::error_message] == "Invalid field 'marker'.",
279 jrr.toStyledString());
280 }
281
282 {
283 // invalid marker - not a string
284 Json::Value jvParams;
285 jvParams[jss::account] = bob.human();
286 jvParams[jss::marker] = 1;
287 auto const jrr = env.rpc(
288 "json",
289 "account_offers",
290 jvParams.toStyledString())[jss::result];
291 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
292 BEAST_EXPECT(jrr[jss::status] == "error");
293 BEAST_EXPECT(
294 jrr[jss::error_message] ==
295 "Invalid field 'marker', not string.");
296 }
297
298 {
299 // ask for a bad ledger index
300 Json::Value jvParams;
301 jvParams[jss::account] = bob.human();
302 jvParams[jss::ledger_index] = 10u;
303 auto const jrr = env.rpc(
304 "json",
305 "account_offers",
306 jvParams.toStyledString())[jss::result];
307 BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
308 BEAST_EXPECT(jrr[jss::status] == "error");
309 BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
310 }
311 }
312
313 void
314 run() override
315 {
316 testSequential(true);
317 testSequential(false);
318 testBadInput();
320 }
321};
322
323BEAST_DEFINE_TESTSUITE(AccountOffers, rpc, ripple);
324
325} // namespace test
326} // namespace ripple
Represents a JSON value.
Definition json_value.h:130
std::string toStyledString() const
bool isString() const
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
void run() override
Runs the suite.
static bool checkMarker(Json::Value const &val)
Immutable cryptographic account descriptor.
Definition Account.h:20
std::string const & human() const
Returns the human readable public key.
Definition Account.h:99
A transaction testing environment.
Definition Env.h:102
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:302
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:772
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:16
@ 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
bool checkArraySize(Json::Value const &val, unsigned int size)
std::unique_ptr< Config > no_admin(std::unique_ptr< Config >)
adjust config so no admin ports are enabled
Definition envconfig.cpp:57
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:35
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
T size(T... args)