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