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