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