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().onStop();
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.get()));
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  BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
374  return jv[jss::type] == "validationReceived" &&
375  jv[jss::validation_public_key].asString() == valPublicKey &&
376  jv[jss::ledger_hash] ==
377  to_string(env.closed()->info().hash) &&
378  jv[jss::ledger_index] ==
379  std::to_string(env.closed()->info().seq) &&
380  jv[jss::flags] ==
381  (vfFullyCanonicalSig | STValidation::kFullFlag) &&
382  jv[jss::full] == true && !jv.isMember(jss::load_fee) &&
383  jv[jss::signature] && jv[jss::signing_time];
384  }));
385  }
386 
387  // RPC unsubscribe
388  auto jv = wsc->invoke("unsubscribe", stream);
389  if (wsc->version() == 2)
390  {
391  BEAST_EXPECT(
392  jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
393  BEAST_EXPECT(
394  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
395  BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
396  }
397  BEAST_EXPECT(jv[jss::status] == "success");
398  }
399 
400  void
402  {
403  using namespace jtx;
404  testcase("Subscribe by url");
405  Env env{*this};
406 
407  Json::Value jv;
408  jv[jss::url] = "http://localhost/events";
409  jv[jss::url_username] = "admin";
410  jv[jss::url_password] = "password";
411  jv[jss::streams] = Json::arrayValue;
412  jv[jss::streams][0u] = "validations";
413  auto jr = env.rpc("json", "subscribe", to_string(jv))[jss::result];
414  BEAST_EXPECT(jr[jss::status] == "success");
415 
416  jv[jss::streams][0u] = "ledger";
417  jr = env.rpc("json", "subscribe", to_string(jv))[jss::result];
418  BEAST_EXPECT(jr[jss::status] == "success");
419 
420  jr = env.rpc("json", "unsubscribe", to_string(jv))[jss::result];
421  BEAST_EXPECT(jr[jss::status] == "success");
422 
423  jv[jss::streams][0u] = "validations";
424  jr = env.rpc("json", "unsubscribe", to_string(jv))[jss::result];
425  BEAST_EXPECT(jr[jss::status] == "success");
426  }
427 
428  void
429  testSubErrors(bool subscribe)
430  {
431  using namespace jtx;
432  auto const method = subscribe ? "subscribe" : "unsubscribe";
433  testcase << "Error cases for " << method;
434 
435  Env env{*this};
436  auto wsc = makeWSClient(env.app().config());
437 
438  {
439  auto jr = env.rpc("json", method, "{}")[jss::result];
440  BEAST_EXPECT(jr[jss::error] == "invalidParams");
441  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
442  }
443 
444  {
445  Json::Value jv;
446  jv[jss::url] = "not-a-url";
447  jv[jss::username] = "admin";
448  jv[jss::password] = "password";
449  auto jr = env.rpc("json", method, to_string(jv))[jss::result];
450  if (subscribe)
451  {
452  BEAST_EXPECT(jr[jss::error] == "invalidParams");
453  BEAST_EXPECT(jr[jss::error_message] == "Failed to parse url.");
454  }
455  // else TODO: why isn't this an error for unsubscribe ?
456  // (findRpcSub returns null)
457  }
458 
459  {
460  Json::Value jv;
461  jv[jss::url] = "ftp://scheme.not.supported.tld";
462  auto jr = env.rpc("json", method, to_string(jv))[jss::result];
463  if (subscribe)
464  {
465  BEAST_EXPECT(jr[jss::error] == "invalidParams");
466  BEAST_EXPECT(
467  jr[jss::error_message] ==
468  "Only http and https is supported.");
469  }
470  }
471 
472  {
473  Env env_nonadmin{*this, no_admin(envconfig(port_increment, 3))};
474  Json::Value jv;
475  jv[jss::url] = "no-url";
476  auto jr =
477  env_nonadmin.rpc("json", method, to_string(jv))[jss::result];
478  BEAST_EXPECT(jr[jss::error] == "noPermission");
479  BEAST_EXPECT(
480  jr[jss::error_message] ==
481  "You don't have permission for this command.");
482  }
483 
484  std::initializer_list<Json::Value> const nonArrays{
489  "",
492 
493  for (auto const& f : {jss::accounts_proposed, jss::accounts})
494  {
495  for (auto const& nonArray : nonArrays)
496  {
497  Json::Value jv;
498  jv[f] = nonArray;
499  auto jr = wsc->invoke(method, jv)[jss::result];
500  BEAST_EXPECT(jr[jss::error] == "invalidParams");
501  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
502  }
503 
504  {
505  Json::Value jv;
506  jv[f] = Json::arrayValue;
507  auto jr = wsc->invoke(method, jv)[jss::result];
508  BEAST_EXPECT(jr[jss::error] == "actMalformed");
509  BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
510  }
511  }
512 
513  for (auto const& nonArray : nonArrays)
514  {
515  Json::Value jv;
516  jv[jss::books] = nonArray;
517  auto jr = wsc->invoke(method, jv)[jss::result];
518  BEAST_EXPECT(jr[jss::error] == "invalidParams");
519  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
520  }
521 
522  {
523  Json::Value jv;
524  jv[jss::books] = Json::arrayValue;
525  jv[jss::books][0u] = 1;
526  auto jr = wsc->invoke(method, jv)[jss::result];
527  BEAST_EXPECT(jr[jss::error] == "invalidParams");
528  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
529  }
530 
531  {
532  Json::Value jv;
533  jv[jss::books] = Json::arrayValue;
534  jv[jss::books][0u] = Json::objectValue;
535  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
536  jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
537  auto jr = wsc->invoke(method, jv)[jss::result];
538  BEAST_EXPECT(jr[jss::error] == "srcCurMalformed");
539  BEAST_EXPECT(
540  jr[jss::error_message] == "Source currency is malformed.");
541  }
542 
543  {
544  Json::Value jv;
545  jv[jss::books] = Json::arrayValue;
546  jv[jss::books][0u] = Json::objectValue;
547  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
548  jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
549  jv[jss::books][0u][jss::taker_pays][jss::currency] = "ZZZZ";
550  auto jr = wsc->invoke(method, jv)[jss::result];
551  BEAST_EXPECT(jr[jss::error] == "srcCurMalformed");
552  BEAST_EXPECT(
553  jr[jss::error_message] == "Source currency is malformed.");
554  }
555 
556  {
557  Json::Value jv;
558  jv[jss::books] = Json::arrayValue;
559  jv[jss::books][0u] = Json::objectValue;
560  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
561  jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
562  jv[jss::books][0u][jss::taker_pays][jss::currency] = "USD";
563  jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
564  auto jr = wsc->invoke(method, jv)[jss::result];
565  BEAST_EXPECT(jr[jss::error] == "srcIsrMalformed");
566  BEAST_EXPECT(
567  jr[jss::error_message] == "Source issuer is malformed.");
568  }
569 
570  {
571  Json::Value jv;
572  jv[jss::books] = Json::arrayValue;
573  jv[jss::books][0u] = Json::objectValue;
574  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
575  jv[jss::books][0u][jss::taker_pays] = Json::objectValue;
576  jv[jss::books][0u][jss::taker_pays][jss::currency] = "USD";
577  jv[jss::books][0u][jss::taker_pays][jss::issuer] =
578  Account{"gateway"}.human() + "DEAD";
579  auto jr = wsc->invoke(method, jv)[jss::result];
580  BEAST_EXPECT(jr[jss::error] == "srcIsrMalformed");
581  BEAST_EXPECT(
582  jr[jss::error_message] == "Source issuer is malformed.");
583  }
584 
585  {
586  Json::Value jv;
587  jv[jss::books] = Json::arrayValue;
588  jv[jss::books][0u] = Json::objectValue;
589  jv[jss::books][0u][jss::taker_pays] =
590  Account{"gateway"}["USD"](1).value().getJson(
592  jv[jss::books][0u][jss::taker_gets] = Json::objectValue;
593  auto jr = wsc->invoke(method, jv)[jss::result];
594  // NOTE: this error is slightly incongruous with the
595  // equivalent source currency error
596  BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed");
597  BEAST_EXPECT(
598  jr[jss::error_message] ==
599  "Destination amount/currency/issuer is malformed.");
600  }
601 
602  {
603  Json::Value jv;
604  jv[jss::books] = Json::arrayValue;
605  jv[jss::books][0u] = Json::objectValue;
606  jv[jss::books][0u][jss::taker_pays] =
607  Account{"gateway"}["USD"](1).value().getJson(
609  jv[jss::books][0u][jss::taker_gets][jss::currency] = "ZZZZ";
610  auto jr = wsc->invoke(method, jv)[jss::result];
611  // NOTE: this error is slightly incongruous with the
612  // equivalent source currency error
613  BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed");
614  BEAST_EXPECT(
615  jr[jss::error_message] ==
616  "Destination amount/currency/issuer is malformed.");
617  }
618 
619  {
620  Json::Value jv;
621  jv[jss::books] = Json::arrayValue;
622  jv[jss::books][0u] = Json::objectValue;
623  jv[jss::books][0u][jss::taker_pays] =
624  Account{"gateway"}["USD"](1).value().getJson(
626  jv[jss::books][0u][jss::taker_gets][jss::currency] = "USD";
627  jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
628  auto jr = wsc->invoke(method, jv)[jss::result];
629  BEAST_EXPECT(jr[jss::error] == "dstIsrMalformed");
630  BEAST_EXPECT(
631  jr[jss::error_message] == "Destination issuer is malformed.");
632  }
633 
634  {
635  Json::Value jv;
636  jv[jss::books] = Json::arrayValue;
637  jv[jss::books][0u] = Json::objectValue;
638  jv[jss::books][0u][jss::taker_pays] =
639  Account{"gateway"}["USD"](1).value().getJson(
641  jv[jss::books][0u][jss::taker_gets][jss::currency] = "USD";
642  jv[jss::books][0u][jss::taker_gets][jss::issuer] =
643  Account{"gateway"}.human() + "DEAD";
644  auto jr = wsc->invoke(method, jv)[jss::result];
645  BEAST_EXPECT(jr[jss::error] == "dstIsrMalformed");
646  BEAST_EXPECT(
647  jr[jss::error_message] == "Destination issuer is malformed.");
648  }
649 
650  {
651  Json::Value jv;
652  jv[jss::books] = Json::arrayValue;
653  jv[jss::books][0u] = Json::objectValue;
654  jv[jss::books][0u][jss::taker_pays] =
655  Account{"gateway"}["USD"](1).value().getJson(
657  jv[jss::books][0u][jss::taker_gets] =
658  Account{"gateway"}["USD"](1).value().getJson(
660  auto jr = wsc->invoke(method, jv)[jss::result];
661  BEAST_EXPECT(jr[jss::error] == "badMarket");
662  BEAST_EXPECT(jr[jss::error_message] == "No such market.");
663  }
664 
665  for (auto const& nonArray : nonArrays)
666  {
667  Json::Value jv;
668  jv[jss::streams] = nonArray;
669  auto jr = wsc->invoke(method, jv)[jss::result];
670  BEAST_EXPECT(jr[jss::error] == "invalidParams");
671  BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
672  }
673 
674  {
675  Json::Value jv;
676  jv[jss::streams] = Json::arrayValue;
677  jv[jss::streams][0u] = 1;
678  auto jr = wsc->invoke(method, jv)[jss::result];
679  BEAST_EXPECT(jr[jss::error] == "malformedStream");
680  BEAST_EXPECT(jr[jss::error_message] == "Stream malformed.");
681  }
682 
683  {
684  Json::Value jv;
685  jv[jss::streams] = Json::arrayValue;
686  jv[jss::streams][0u] = "not_a_stream";
687  auto jr = wsc->invoke(method, jv)[jss::result];
688  BEAST_EXPECT(jr[jss::error] == "malformedStream");
689  BEAST_EXPECT(jr[jss::error_message] == "Stream malformed.");
690  }
691  }
692 
693  void
694  run() override
695  {
696  testServer();
697  testLedger();
699  testManifests();
700  testValidations();
701  testSubErrors(true);
702  testSubErrors(false);
703  testSubByUrl();
704  }
705 };
706 
707 BEAST_DEFINE_TESTSUITE(Subscribe, app, ripple);
708 
709 } // namespace test
710 } // 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::LoadManager::onStop
void onStop() override
Override called when the stop notification is issued.
Definition: LoadManager.cpp:95
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:238
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
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:232
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::derivePublicKey
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
Definition: SecretKey.cpp:205
ripple::Application::config
virtual Config & config()=0
ripple::NetworkOPs::reportFeeChange
virtual void reportFeeChange()=0
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:179
ripple::test::Subscribe_test::run
void run() override
Definition: Subscribe_test.cpp:694
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
ripple::test::jtx::Env::close
void close(NetClock::time_point closeTime, boost::optional< std::chrono::milliseconds > consensusDelay=boost::none)
Close and advance the ledger.
Definition: Env.cpp:111
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:201
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:299
ripple::TokenType::NodePublic
@ NodePublic
ripple::test::Subscribe_test::testSubByUrl
void testSubByUrl()
Definition: Subscribe_test.cpp:401
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:429
ripple::test::Subscribe_test::testValidations
void testValidations()
Definition: Subscribe_test.cpp:329
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:114
Json::Value
Represents a JSON value.
Definition: json_value.h:145
std::initializer_list