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