rippled
Subscribe_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5  Permission to use, copy, modify, and/or distribute this software for any
6  purpose with or without fee is hereby granted, provided that the above
7  copyright notice and this permission notice appear in all copies.
8  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 //==============================================================================
17 
18 #include <ripple/app/main/LoadManager.h>
19 #include <ripple/app/misc/LoadFeeTrack.h>
20 #include <ripple/app/misc/NetworkOPs.h>
21 #include <ripple/beast/unit_test.h>
22 #include <ripple/core/ConfigSections.h>
23 #include <ripple/protocol/jss.h>
24 #include <test/jtx.h>
25 #include <test/jtx/WSClient.h>
26 #include <test/jtx/envconfig.h>
27 
28 namespace ripple {
29 namespace test {
30 
31 class Subscribe_test : public beast::unit_test::suite
32 {
33 public:
34  void
36  {
37  using namespace std::chrono_literals;
38  using namespace jtx;
39  Env env(*this);
40  auto wsc = makeWSClient(env.app().config());
41  Json::Value stream;
42 
43  {
44  // RPC subscribe to server stream
45  stream[jss::streams] = Json::arrayValue;
46  stream[jss::streams].append("server");
47  auto jv = wsc->invoke("subscribe", stream);
48  if (wsc->version() == 2)
49  {
50  BEAST_EXPECT(
51  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
52  BEAST_EXPECT(
53  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
54  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
55  }
56  BEAST_EXPECT(jv[jss::status] == "success");
57  }
58 
59  // here we forcibly stop the load manager because it can (rarely but
60  // every-so-often) cause fees to raise or lower AFTER we've called the
61  // first findMsg but BEFORE we unsubscribe, thus causing the final
62  // findMsg check to fail since there is one unprocessed ws msg created
63  // by the loadmanager
64  env.app().getLoadManager().stop();
65  {
66  // Raise fee to cause an update
67  auto& feeTrack = env.app().getFeeTrack();
68  for (int i = 0; i < 5; ++i)
69  feeTrack.raiseLocalFee();
70  env.app().getOPs().reportFeeChange();
71 
72  // Check stream update
73  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
74  return jv[jss::type] == "serverStatus";
75  }));
76  }
77 
78  {
79  // RPC unsubscribe
80  auto jv = wsc->invoke("unsubscribe", stream);
81  if (wsc->version() == 2)
82  {
83  BEAST_EXPECT(
84  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
85  BEAST_EXPECT(
86  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
87  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
88  }
89  BEAST_EXPECT(jv[jss::status] == "success");
90  }
91 
92  {
93  // Raise fee to cause an update
94  auto& feeTrack = env.app().getFeeTrack();
95  for (int i = 0; i < 5; ++i)
96  feeTrack.raiseLocalFee();
97  env.app().getOPs().reportFeeChange();
98 
99  // Check stream update
100  auto jvo = wsc->getMsg(10ms);
101  BEAST_EXPECTS(!jvo, "getMsg: " + to_string(jvo.value()));
102  }
103  }
104 
105  void
107  {
108  using namespace std::chrono_literals;
109  using namespace jtx;
110  Env env(*this);
111  auto wsc = makeWSClient(env.app().config());
112  Json::Value stream;
113 
114  {
115  // RPC subscribe to ledger stream
116  stream[jss::streams] = Json::arrayValue;
117  stream[jss::streams].append("ledger");
118  auto jv = wsc->invoke("subscribe", stream);
119  if (wsc->version() == 2)
120  {
121  BEAST_EXPECT(
122  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
123  BEAST_EXPECT(
124  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
125  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
126  }
127  BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 2);
128  }
129 
130  {
131  // Accept a ledger
132  env.close();
133 
134  // Check stream update
135  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
136  return jv[jss::ledger_index] == 3;
137  }));
138  }
139 
140  {
141  // Accept another ledger
142  env.close();
143 
144  // Check stream update
145  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
146  return jv[jss::ledger_index] == 4;
147  }));
148  }
149 
150  // RPC unsubscribe
151  auto jv = wsc->invoke("unsubscribe", stream);
152  if (wsc->version() == 2)
153  {
154  BEAST_EXPECT(
155  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
156  BEAST_EXPECT(
157  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
158  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
159  }
160  BEAST_EXPECT(jv[jss::status] == "success");
161  }
162 
163  void
165  {
166  using namespace std::chrono_literals;
167  using namespace jtx;
168  Env env(*this);
169  auto wsc = makeWSClient(env.app().config());
170  Json::Value stream;
171 
172  {
173  // RPC subscribe to transactions stream
174  stream[jss::streams] = Json::arrayValue;
175  stream[jss::streams].append("transactions");
176  auto jv = wsc->invoke("subscribe", stream);
177  if (wsc->version() == 2)
178  {
179  BEAST_EXPECT(
180  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
181  BEAST_EXPECT(
182  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
183  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
184  }
185  BEAST_EXPECT(jv[jss::status] == "success");
186  }
187 
188  {
189  env.fund(XRP(10000), "alice");
190  env.close();
191 
192  // Check stream update for payment transaction
193  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
194  return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]
195  ["NewFields"][jss::Account] ==
196  Account("alice").human();
197  }));
198 
199  // Check stream update for accountset transaction
200  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
201  return jv[jss::meta]["AffectedNodes"][0u]["ModifiedNode"]
202  ["FinalFields"][jss::Account] ==
203  Account("alice").human();
204  }));
205 
206  env.fund(XRP(10000), "bob");
207  env.close();
208 
209  // Check stream update for payment transaction
210  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
211  return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]
212  ["NewFields"][jss::Account] == Account("bob").human();
213  }));
214 
215  // Check stream update for accountset transaction
216  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
217  return jv[jss::meta]["AffectedNodes"][0u]["ModifiedNode"]
218  ["FinalFields"][jss::Account] ==
219  Account("bob").human();
220  }));
221  }
222 
223  {
224  // RPC unsubscribe
225  auto jv = wsc->invoke("unsubscribe", stream);
226  if (wsc->version() == 2)
227  {
228  BEAST_EXPECT(
229  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
230  BEAST_EXPECT(
231  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
232  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
233  }
234  BEAST_EXPECT(jv[jss::status] == "success");
235  }
236 
237  {
238  // RPC subscribe to accounts stream
239  stream = Json::objectValue;
240  stream[jss::accounts] = Json::arrayValue;
241  stream[jss::accounts].append(Account("alice").human());
242  auto jv = wsc->invoke("subscribe", stream);
243  if (wsc->version() == 2)
244  {
245  BEAST_EXPECT(
246  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
247  BEAST_EXPECT(
248  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
249  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
250  }
251  BEAST_EXPECT(jv[jss::status] == "success");
252  }
253 
254  {
255  // Transaction that does not affect stream
256  env.fund(XRP(10000), "carol");
257  env.close();
258  BEAST_EXPECT(!wsc->getMsg(10ms));
259 
260  // Transactions concerning alice
261  env.trust(Account("bob")["USD"](100), "alice");
262  env.close();
263 
264  // Check stream updates
265  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
266  return jv[jss::meta]["AffectedNodes"][1u]["ModifiedNode"]
267  ["FinalFields"][jss::Account] ==
268  Account("alice").human();
269  }));
270 
271  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
272  return jv[jss::meta]["AffectedNodes"][1u]["CreatedNode"]
273  ["NewFields"]["LowLimit"][jss::issuer] ==
274  Account("alice").human();
275  }));
276  }
277 
278  // RPC unsubscribe
279  auto jv = wsc->invoke("unsubscribe", stream);
280  if (wsc->version() == 2)
281  {
282  BEAST_EXPECT(
283  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
284  BEAST_EXPECT(
285  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
286  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
287  }
288  BEAST_EXPECT(jv[jss::status] == "success");
289  }
290 
291  void
293  {
294  using namespace jtx;
295  Env env(*this);
296  auto wsc = makeWSClient(env.app().config());
297  Json::Value stream;
298 
299  {
300  // RPC subscribe to manifests stream
301  stream[jss::streams] = Json::arrayValue;
302  stream[jss::streams].append("manifests");
303  auto jv = wsc->invoke("subscribe", stream);
304  if (wsc->version() == 2)
305  {
306  BEAST_EXPECT(
307  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
308  BEAST_EXPECT(
309  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
310  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
311  }
312  BEAST_EXPECT(jv[jss::status] == "success");
313  }
314 
315  // RPC unsubscribe
316  auto jv = wsc->invoke("unsubscribe", stream);
317  if (wsc->version() == 2)
318  {
319  BEAST_EXPECT(
320  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
321  BEAST_EXPECT(
322  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
323  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
324  }
325  BEAST_EXPECT(jv[jss::status] == "success");
326  }
327 
328  void
330  {
331  using namespace jtx;
332 
333  Env env{*this, envconfig(validator, "")};
334  auto& cfg = env.app().config();
335  if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
336  return;
337  auto const parsedseed =
338  parseBase58<Seed>(cfg.section(SECTION_VALIDATION_SEED).values()[0]);
339  if (!BEAST_EXPECT(parsedseed))
340  return;
341 
342  std::string const valPublicKey = toBase58(
346  generateSecretKey(KeyType::secp256k1, *parsedseed)));
347 
348  auto wsc = makeWSClient(env.app().config());
349  Json::Value stream;
350 
351  {
352  // RPC subscribe to validations stream
353  stream[jss::streams] = Json::arrayValue;
354  stream[jss::streams].append("validations");
355  auto jv = wsc->invoke("subscribe", stream);
356  if (wsc->version() == 2)
357  {
358  BEAST_EXPECT(
359  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
360  BEAST_EXPECT(
361  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
362  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
363  }
364  BEAST_EXPECT(jv[jss::status] == "success");
365  }
366 
367  {
368  // Accept a ledger
369  env.close();
370 
371  // Check stream update
372  using namespace std::chrono_literals;
373 
374  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
375  return jv[jss::type] == "validationReceived" &&
376  jv[jss::validation_public_key].asString() == valPublicKey &&
377  jv[jss::ledger_hash] ==
378  to_string(env.closed()->info().hash) &&
379  jv[jss::ledger_index] ==
380  std::to_string(env.closed()->info().seq) &&
381  jv[jss::flags] ==
382  (vfFullyCanonicalSig | vfFullValidation) &&
383  jv[jss::full] == true && !jv.isMember(jss::load_fee) &&
384  jv[jss::signature] && jv[jss::signing_time];
385  }));
386  }
387 
388  // RPC unsubscribe
389  auto jv = wsc->invoke("unsubscribe", stream);
390  if (wsc->version() == 2)
391  {
392  BEAST_EXPECT(
393  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
394  BEAST_EXPECT(
395  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
396  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
397  }
398  BEAST_EXPECT(jv[jss::status] == "success");
399  }
400 
401  void
403  {
404  using namespace jtx;
405  testcase("Subscribe by url");
406  Env env{*this};
407 
408  Json::Value jv;
409  jv[jss::url] = "http://localhost/events";
410  jv[jss::url_username] = "admin";
411  jv[jss::url_password] = "password";
412  jv[jss::streams] = Json::arrayValue;
413  jv[jss::streams][0u] = "validations";
414  auto jr = env.rpc("json", "subscribe", to_string(jv))[jss::result];
415  BEAST_EXPECT(jr[jss::status] == "success");
416 
417  jv[jss::streams][0u] = "ledger";
418  jr = env.rpc("json", "subscribe", to_string(jv))[jss::result];
419  BEAST_EXPECT(jr[jss::status] == "success");
420 
421  jr = env.rpc("json", "unsubscribe", to_string(jv))[jss::result];
422  BEAST_EXPECT(jr[jss::status] == "success");
423 
424  jv[jss::streams][0u] = "validations";
425  jr = env.rpc("json", "unsubscribe", to_string(jv))[jss::result];
426  BEAST_EXPECT(jr[jss::status] == "success");
427  }
428 
429  void
430  testSubErrors(bool subscribe)
431  {
432  using namespace jtx;
433  auto const method = subscribe ? "subscribe" : "unsubscribe";
434  testcase << "Error cases for " << method;
435 
436  Env env{*this};
437  auto wsc = makeWSClient(env.app().config());
438 
439  {
440  auto jr = env.rpc("json", method, "{}")[jss::result];
441  BEAST_EXPECT(jr[jss::error] == "invalidParams");
442  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
443  }
444 
445  {
446  Json::Value jv;
447  jv[jss::url] = "not-a-url";
448  jv[jss::username] = "admin";
449  jv[jss::password] = "password";
450  auto jr = env.rpc("json", method, to_string(jv))[jss::result];
451  if (subscribe)
452  {
453  BEAST_EXPECT(jr[jss::error] == "invalidParams");
454  BEAST_EXPECT(jr[jss::error_message] == "Failed to parse url.");
455  }
456  // else TODO: why isn't this an error for unsubscribe ?
457  // (findRpcSub returns null)
458  }
459 
460  {
461  Json::Value jv;
462  jv[jss::url] = "ftp://scheme.not.supported.tld";
463  auto jr = env.rpc("json", method, to_string(jv))[jss::result];
464  if (subscribe)
465  {
466  BEAST_EXPECT(jr[jss::error] == "invalidParams");
467  BEAST_EXPECT(
468  jr[jss::error_message] ==
469  "Only http and https is supported.");
470  }
471  }
472 
473  {
474  Env env_nonadmin{*this, no_admin(envconfig(port_increment, 3))};
475  Json::Value jv;
476  jv[jss::url] = "no-url";
477  auto jr =
478  env_nonadmin.rpc("json", method, to_string(jv))[jss::result];
479  BEAST_EXPECT(jr[jss::error] == "noPermission");
480  BEAST_EXPECT(
481  jr[jss::error_message] ==
482  "You don't have permission for this command.");
483  }
484 
485  std::initializer_list<Json::Value> const nonArrays{
490  "",
493 
494  for (auto const& f : {jss::accounts_proposed, jss::accounts})
495  {
496  for (auto const& nonArray : nonArrays)
497  {
498  Json::Value jv;
499  jv[f] = nonArray;
500  auto jr = wsc->invoke(method, jv)[jss::result];
501  BEAST_EXPECT(jr[jss::error] == "invalidParams");
502  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
503  }
504 
505  {
506  Json::Value jv;
507  jv[f] = Json::arrayValue;
508  auto jr = wsc->invoke(method, jv)[jss::result];
509  BEAST_EXPECT(jr[jss::error] == "actMalformed");
510  BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
511  }
512  }
513 
514  for (auto const& nonArray : nonArrays)
515  {
516  Json::Value jv;
517  jv[jss::books] = nonArray;
518  auto jr = wsc->invoke(method, jv)[jss::result];
519  BEAST_EXPECT(jr[jss::error] == "invalidParams");
520  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
521  }
522 
523  {
524  Json::Value jv;
525  jv[jss::books] = Json::arrayValue;
526  jv[jss::books][0u] = 1;
527  auto jr = wsc->invoke(method, jv)[jss::result];
528  BEAST_EXPECT(jr[jss::error] == "invalidParams");
529  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
530  }
531 
532  {
533  Json::Value jv;
534  jv[jss::books] = Json::arrayValue;
535  jv[jss::books][0u] = Json::objectValue;
536  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
537  jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
538  auto jr = wsc->invoke(method, jv)[jss::result];
539  BEAST_EXPECT(jr[jss::error] == "srcCurMalformed");
540  BEAST_EXPECT(
541  jr[jss::error_message] == "Source currency is malformed.");
542  }
543 
544  {
545  Json::Value jv;
546  jv[jss::books] = Json::arrayValue;
547  jv[jss::books][0u] = Json::objectValue;
548  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
549  jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
550  jv[jss::books][0u][jss::taker_pays][jss::currency] = "ZZZZ";
551  auto jr = wsc->invoke(method, jv)[jss::result];
552  BEAST_EXPECT(jr[jss::error] == "srcCurMalformed");
553  BEAST_EXPECT(
554  jr[jss::error_message] == "Source currency is malformed.");
555  }
556 
557  {
558  Json::Value jv;
559  jv[jss::books] = Json::arrayValue;
560  jv[jss::books][0u] = Json::objectValue;
561  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
562  jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
563  jv[jss::books][0u][jss::taker_pays][jss::currency] = "USD";
564  jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
565  auto jr = wsc->invoke(method, jv)[jss::result];
566  BEAST_EXPECT(jr[jss::error] == "srcIsrMalformed");
567  BEAST_EXPECT(
568  jr[jss::error_message] == "Source issuer is malformed.");
569  }
570 
571  {
572  Json::Value jv;
573  jv[jss::books] = Json::arrayValue;
574  jv[jss::books][0u] = Json::objectValue;
575  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
576  jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
577  jv[jss::books][0u][jss::taker_pays][jss::currency] = "USD";
578  jv[jss::books][0u][jss::taker_pays][jss::issuer] =
579  Account{"gateway"}.human() + "DEAD";
580  auto jr = wsc->invoke(method, jv)[jss::result];
581  BEAST_EXPECT(jr[jss::error] == "srcIsrMalformed");
582  BEAST_EXPECT(
583  jr[jss::error_message] == "Source issuer is malformed.");
584  }
585 
586  {
587  Json::Value jv;
588  jv[jss::books] = Json::arrayValue;
589  jv[jss::books][0u] = Json::objectValue;
590  jv[jss::books][0u][jss::taker_pays] =
591  Account{"gateway"}["USD"](1).value().getJson(
593  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
594  auto jr = wsc->invoke(method, jv)[jss::result];
595  // NOTE: this error is slightly incongruous with the
596  // equivalent source currency error
597  BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed");
598  BEAST_EXPECT(
599  jr[jss::error_message] ==
600  "Destination amount/currency/issuer is malformed.");
601  }
602 
603  {
604  Json::Value jv;
605  jv[jss::books] = Json::arrayValue;
606  jv[jss::books][0u] = Json::objectValue;
607  jv[jss::books][0u][jss::taker_pays] =
608  Account{"gateway"}["USD"](1).value().getJson(
610  jv[jss::books][0u][jss::taker_gets][jss::currency] = "ZZZZ";
611  auto jr = wsc->invoke(method, jv)[jss::result];
612  // NOTE: this error is slightly incongruous with the
613  // equivalent source currency error
614  BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed");
615  BEAST_EXPECT(
616  jr[jss::error_message] ==
617  "Destination amount/currency/issuer is malformed.");
618  }
619 
620  {
621  Json::Value jv;
622  jv[jss::books] = Json::arrayValue;
623  jv[jss::books][0u] = Json::objectValue;
624  jv[jss::books][0u][jss::taker_pays] =
625  Account{"gateway"}["USD"](1).value().getJson(
627  jv[jss::books][0u][jss::taker_gets][jss::currency] = "USD";
628  jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
629  auto jr = wsc->invoke(method, jv)[jss::result];
630  BEAST_EXPECT(jr[jss::error] == "dstIsrMalformed");
631  BEAST_EXPECT(
632  jr[jss::error_message] == "Destination issuer is malformed.");
633  }
634 
635  {
636  Json::Value jv;
637  jv[jss::books] = Json::arrayValue;
638  jv[jss::books][0u] = Json::objectValue;
639  jv[jss::books][0u][jss::taker_pays] =
640  Account{"gateway"}["USD"](1).value().getJson(
642  jv[jss::books][0u][jss::taker_gets][jss::currency] = "USD";
643  jv[jss::books][0u][jss::taker_gets][jss::issuer] =
644  Account{"gateway"}.human() + "DEAD";
645  auto jr = wsc->invoke(method, jv)[jss::result];
646  BEAST_EXPECT(jr[jss::error] == "dstIsrMalformed");
647  BEAST_EXPECT(
648  jr[jss::error_message] == "Destination issuer is malformed.");
649  }
650 
651  {
652  Json::Value jv;
653  jv[jss::books] = Json::arrayValue;
654  jv[jss::books][0u] = Json::objectValue;
655  jv[jss::books][0u][jss::taker_pays] =
656  Account{"gateway"}["USD"](1).value().getJson(
658  jv[jss::books][0u][jss::taker_gets] =
659  Account{"gateway"}["USD"](1).value().getJson(
661  auto jr = wsc->invoke(method, jv)[jss::result];
662  BEAST_EXPECT(jr[jss::error] == "badMarket");
663  BEAST_EXPECT(jr[jss::error_message] == "No such market.");
664  }
665 
666  for (auto const& nonArray : nonArrays)
667  {
668  Json::Value jv;
669  jv[jss::streams] = nonArray;
670  auto jr = wsc->invoke(method, jv)[jss::result];
671  BEAST_EXPECT(jr[jss::error] == "invalidParams");
672  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
673  }
674 
675  {
676  Json::Value jv;
677  jv[jss::streams] = Json::arrayValue;
678  jv[jss::streams][0u] = 1;
679  auto jr = wsc->invoke(method, jv)[jss::result];
680  BEAST_EXPECT(jr[jss::error] == "malformedStream");
681  BEAST_EXPECT(jr[jss::error_message] == "Stream malformed.");
682  }
683 
684  {
685  Json::Value jv;
686  jv[jss::streams] = Json::arrayValue;
687  jv[jss::streams][0u] = "not_a_stream";
688  auto jr = wsc->invoke(method, jv)[jss::result];
689  BEAST_EXPECT(jr[jss::error] == "malformedStream");
690  BEAST_EXPECT(jr[jss::error_message] == "Stream malformed.");
691  }
692  }
693 
694  void
695  run() override
696  {
697  testServer();
698  testLedger();
700  testManifests();
701  testValidations();
702  testSubErrors(true);
703  testSubErrors(false);
704  testSubByUrl();
705  }
706 };
707 
708 BEAST_DEFINE_TESTSUITE(Subscribe, app, ripple);
709 
710 } // namespace test
711 } // namespace ripple
ripple::JsonOptions::include_date
@ include_date
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
ripple::test::Subscribe_test::testLedger
void testLedger()
Definition: Subscribe_test.cpp:106
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountDelete, app, ripple)
std::string
STL class.
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
Json::booleanValue
@ booleanValue
bool value
Definition: json_value.h:41
ripple::test::jtx::validator
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
Definition: envconfig.cpp:89
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::test::jtx::port_increment
std::unique_ptr< Config > port_increment(std::unique_ptr< Config >, int)
adjust the default configured server ports by a specified value
Definition: envconfig.cpp:98
ripple::test::jtx::Account::human
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:109
Json::realValue
@ realValue
double value
Definition: json_value.h:39
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:240
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::test::jtx::Env::trust
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:256
ripple::Application::getOPs
virtual NetworkOPs & getOPs()=0
ripple::Application::getFeeTrack
virtual LoadFeeTrack & getFeeTrack()=0
Json::uintValue
@ uintValue
unsigned integer value
Definition: json_value.h:38
ripple::test::Subscribe_test
Definition: Subscribe_test.cpp:31
ripple::test::jtx::no_admin
std::unique_ptr< Config > no_admin(std::unique_ptr< Config >)
adjust config so no admin ports are enabled
Definition: envconfig.cpp:70
ripple::Application::getLoadManager
virtual LoadManager & getLoadManager()=0
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::LoadManager::stop
void stop()
Definition: LoadManager.cpp:82
ripple::derivePublicKey
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
Definition: SecretKey.cpp:313
ripple::Application::config
virtual Config & config()=0
ripple::NetworkOPs::reportFeeChange
virtual void reportFeeChange()=0
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::test::Subscribe_test::testServer
void testServer()
Definition: Subscribe_test.cpp:35
ripple::generateSecretKey
SecretKey generateSecretKey(KeyType type, Seed const &seed)
Generate a new secret key deterministically.
Definition: SecretKey.cpp:291
ripple::test::Subscribe_test::run
void run() override
Definition: Subscribe_test.cpp:695
ripple::KeyType::secp256k1
@ secp256k1
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::Subscribe_test::testTransactions
void testTransactions()
Definition: Subscribe_test.cpp:164
ripple::test::Subscribe_test::testManifests
void testManifests()
Definition: Subscribe_test.cpp:292
ripple::LoadFeeTrack::raiseLocalFee
bool raiseLocalFee()
Definition: LoadFeeTrack.cpp:37
Json::intValue
@ intValue
signed integer value
Definition: json_value.h:37
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:225
Json::nullValue
@ nullValue
'null' value
Definition: json_value.h:36
ripple::test::makeWSClient
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition: WSClient.cpp:300
ripple::TokenType::NodePublic
@ NodePublic
ripple::test::Subscribe_test::testSubByUrl
void testSubByUrl()
Definition: Subscribe_test.cpp:402
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:39
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::test::Subscribe_test::testSubErrors
void testSubErrors(bool subscribe)
Definition: Subscribe_test.cpp:430
ripple::test::Subscribe_test::testValidations
void testValidations()
Definition: Subscribe_test.cpp:329
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:115
Json::Value
Represents a JSON value.
Definition: json_value.h:145
std::initializer_list