xrpld
Loading...
Searching...
No Matches
STParsedJSON_test.cpp
1
2#include <test/jtx/Env.h>
3
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/unit_test/suite.h>
6#include <xrpl/json/json_forwards.h>
7#include <xrpl/json/json_reader.h>
8#include <xrpl/json/json_value.h>
9#include <xrpl/json/to_string.h>
10#include <xrpl/protocol/AccountID.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/STAmount.h>
13#include <xrpl/protocol/STArray.h>
14#include <xrpl/protocol/STNumber.h>
15#include <xrpl/protocol/STParsedJSON.h>
16#include <xrpl/protocol/STXChainBridge.h>
17#include <xrpl/protocol/UintTypes.h>
18#include <xrpl/protocol/jss.h>
19
20#include <algorithm>
21#include <array>
22#include <cstddef>
23#include <cstdint>
24#include <memory>
25#include <stdexcept>
26#include <string>
27
28namespace xrpl {
29
31{
32 static bool
34 {
35 json::Reader reader;
36 return reader.parse(json, to) && to.isObject();
37 }
38
39 void
41 {
42 testcase("UInt8");
43 {
45 j[sfCloseResolution] = 255;
46 STParsedJSONObject obj("Test", j);
47 BEAST_EXPECT(obj.object.has_value());
48 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
49 BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution));
50 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
51 BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255);
52 }
53
54 // test with uint value
55 {
57 j[sfCloseResolution] = 255u;
58 STParsedJSONObject obj("Test", j);
59 BEAST_EXPECT(obj.object.has_value());
60 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
61 BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution));
62 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
63 BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255);
64 }
65
66 // Test with string value
67 {
69 j[sfCloseResolution] = "255";
70 STParsedJSONObject obj("Test", j);
71 BEAST_EXPECT(obj.object.has_value());
72 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
73 BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution));
74 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
75 BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255);
76 }
77
78 // Test min value for uint8
79 {
81 j[sfCloseResolution] = 0;
82 STParsedJSONObject obj("Test", j);
83 BEAST_EXPECT(obj.object.has_value());
84 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
85 BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 0);
86 }
87
88 // Test out of range value for UInt8 (negative)
89 {
91 j[sfCloseResolution] = -1;
92 STParsedJSONObject const obj("Test", j);
93 BEAST_EXPECT(!obj.object.has_value());
94 }
95
96 // Test out of range value for UInt8 (too large)
97 {
99 j[sfCloseResolution] = 256;
100 STParsedJSONObject const obj("Test", j);
101 BEAST_EXPECT(!obj.object.has_value());
102 }
103
104 // Test bad_type (not a string/int/uint)
105 {
106 json::Value j;
107 j[sfCloseResolution] = json::Value(json::ValueType::Array);
108 STParsedJSONObject const obj("Test", j);
109 BEAST_EXPECT(!obj.object.has_value());
110 }
111
112 // Test bad_type (not a string/int/uint)
113 {
114 json::Value j;
115 j[sfCloseResolution] = json::Value(json::ValueType::Object);
116 STParsedJSONObject const obj("Test", j);
117 BEAST_EXPECT(!obj.object.has_value());
118 }
119 }
120
121 void
123 {
124 testcase("UInt16");
125 // Test with int value
126 {
127 json::Value j;
128 j[sfLedgerEntryType] = 65535;
129 STParsedJSONObject obj("Test", j);
130 BEAST_EXPECT(obj.object.has_value());
131 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
132 BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType));
133 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
134 BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535);
135 }
136
137 // Test with uint value
138 {
139 json::Value j;
140 j[sfLedgerEntryType] = 65535u;
141 STParsedJSONObject obj("Test", j);
142 BEAST_EXPECT(obj.object.has_value());
143 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
144 BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType));
145 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
146 BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535);
147 }
148
149 // Test with string value
150 {
151 json::Value j;
152 j[sfLedgerEntryType] = "65535";
153 STParsedJSONObject obj("Test", j);
154 BEAST_EXPECT(obj.object.has_value());
155 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
156 BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType));
157 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
158 BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535);
159 }
160
161 // Test min value for uint16
162 {
163 json::Value j;
164 j[sfLedgerEntryType] = 0;
165 STParsedJSONObject obj("Test", j);
166 BEAST_EXPECT(obj.object.has_value());
167 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
168 BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 0);
169 }
170
171 // Test out of range value for UInt16 (negative)
172 {
173 json::Value j;
174 j[sfLedgerEntryType] = -1;
175 STParsedJSONObject const obj("Test", j);
176 BEAST_EXPECT(!obj.object.has_value());
177 }
178
179 // Test out of range value for UInt16 (too large)
180 {
181 json::Value j;
182 j[sfLedgerEntryType] = 65536;
183 STParsedJSONObject const obj("Test", j);
184 BEAST_EXPECT(!obj.object.has_value());
185 }
186
187 // Test string value out of range
188 {
189 json::Value j;
190 j[sfLedgerEntryType] = "65536";
191 STParsedJSONObject const obj("Test", j);
192 BEAST_EXPECT(!obj.object.has_value());
193 }
194
195 // Test bad_type (not a string/int/uint)
196 {
197 json::Value j;
198 j[sfLedgerEntryType] = json::Value(json::ValueType::Array);
199 STParsedJSONObject const obj("Test", j);
200 BEAST_EXPECT(!obj.object.has_value());
201 }
202
203 // Test bad_type (not a string/int/uint)
204 {
205 json::Value j;
206 j[sfLedgerEntryType] = json::Value(json::ValueType::Object);
207 STParsedJSONObject const obj("Test", j);
208 BEAST_EXPECT(!obj.object.has_value());
209 }
210
211 // Invalid input for other field
212 {
213 json::Value j;
214 j[sfTransferFee] = "Payment";
215 STParsedJSONObject const obj("Test", j);
216 BEAST_EXPECT(!obj.object.has_value());
217 }
218 }
219
220 void
222 {
223 testcase("UInt32");
224 {
225 json::Value j;
226 j[sfNetworkID] = 4294967295u;
227 STParsedJSONObject obj("Test", j);
228 BEAST_EXPECT(obj.object.has_value());
229 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
230 BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID));
231 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
232 BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u);
233 }
234
235 // Test with string value
236 {
237 json::Value j;
238 j[sfNetworkID] = "4294967295";
239 STParsedJSONObject obj("Test", j);
240 BEAST_EXPECT(obj.object.has_value());
241 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
242 BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID));
243 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
244 BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u);
245 }
246
247 // Test min value for uint32
248 {
249 json::Value j;
250 j[sfNetworkID] = 0;
251 STParsedJSONObject obj("Test", j);
252 BEAST_EXPECT(obj.object.has_value());
253 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
254 BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 0);
255 }
256
257 // Test out of range value for uint32 (negative)
258 {
259 json::Value j;
260 j[sfNetworkID] = -1;
261 STParsedJSONObject const obj("Test", j);
262 BEAST_EXPECT(!obj.object.has_value());
263 }
264
265 // Test string value out of range
266 {
267 json::Value j;
268 j[sfNetworkID] = "4294967296";
269 STParsedJSONObject const obj("Test", j);
270 BEAST_EXPECT(!obj.object.has_value());
271 }
272
273 // Test bad_type (arrayValue)
274 {
275 json::Value j;
276 j[sfNetworkID] = json::Value(json::ValueType::Array);
277 STParsedJSONObject const obj("Test", j);
278 BEAST_EXPECT(!obj.object.has_value());
279 }
280
281 // Test bad_type (objectValue)
282 {
283 json::Value j;
284 j[sfNetworkID] = json::Value(json::ValueType::Object);
285 STParsedJSONObject const obj("Test", j);
286 BEAST_EXPECT(!obj.object.has_value());
287 }
288 }
289
290 void
292 {
293 testcase("UInt64");
294 {
295 json::Value j;
296 j[sfIndexNext] = "ffffffffffffffff";
297 STParsedJSONObject obj("Test", j);
298 BEAST_EXPECT(obj.object.has_value());
299 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
300 BEAST_EXPECT(obj.object->isFieldPresent(sfIndexNext));
301 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
302 BEAST_EXPECT(obj.object->getFieldU64(sfIndexNext) == 18446744073709551615ull);
303 }
304
305 // Test min value for uint64
306 {
307 json::Value j;
308 j[sfIndexNext] = 0;
309 STParsedJSONObject obj("Test", j);
310 BEAST_EXPECT(obj.object.has_value());
311 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
312 BEAST_EXPECT(obj.object->getFieldU64(sfIndexNext) == 0ull);
313 }
314
315 // Test out of range value for uint64 (negative)
316 {
317 json::Value j;
318 j[sfIndexNext] = -1;
319 STParsedJSONObject const obj("Test", j);
320 BEAST_EXPECT(!obj.object.has_value());
321 }
322
323 // NOTE: the JSON parser doesn't support > UInt32, so those values must
324 // be in hex
325 // Test string value out of range
326 // string is interpreted as hex
327 {
328 json::Value j;
329 j[sfIndexNext] = "10000000000000000"; // uint64 max + 1 (in hex)
330 STParsedJSONObject const obj("Test", j);
331 BEAST_EXPECT(!obj.object.has_value());
332 }
333
334 // Test hex string value with 0x prefix (should fail)
335 {
336 json::Value j;
337 j[sfIndexNext] = "0xabcdefabcdef";
338 STParsedJSONObject const obj("Test", j);
339 BEAST_EXPECT(!obj.object.has_value());
340 }
341
342 // Test hex string value with invalid characters
343 {
344 json::Value j;
345 j[sfIndexNext] = "abcdefga";
346 STParsedJSONObject const obj("Test", j);
347 BEAST_EXPECT(!obj.object.has_value());
348 }
349
350 // test arrayValue
351 {
352 json::Value j;
353 j[sfIndexNext] = json::Value(json::ValueType::Array);
354 STParsedJSONObject const obj("Test", j);
355 BEAST_EXPECT(!obj.object.has_value());
356 }
357
358 // test objectValue
359 {
360 json::Value j;
361 j[sfIndexNext] = json::Value(json::ValueType::Object);
362 STParsedJSONObject const obj("Test", j);
363 BEAST_EXPECT(!obj.object.has_value());
364 }
365 }
366
367 void
369 {
370 testcase("UInt128");
371 {
372 json::Value j;
373 j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF";
374 STParsedJSONObject obj("Test", j);
375 BEAST_EXPECT(obj.object.has_value());
376 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
377 BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash));
378 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
379 BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16);
380 std::array<uint8_t, 16> const expected = {
381 0x01,
382 0x23,
383 0x45,
384 0x67,
385 0x89,
386 0xAB,
387 0xCD,
388 0xEF,
389 0x01,
390 0x23,
391 0x45,
392 0x67,
393 0x89,
394 0xAB,
395 0xCD,
396 0xEF};
397 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
398 BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash) == uint128::fromRaw(expected));
399 }
400
401 // Valid lowercase hex string for UInt128
402 {
403 json::Value j;
404 j[sfEmailHash] = "0123456789abcdef0123456789abcdef";
405 STParsedJSONObject obj("Test", j);
406 BEAST_EXPECT(obj.object.has_value());
407 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
408 BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash));
409 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
410 BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16);
411 }
412
413 // Empty string for UInt128 (should be valid, all zero)
414 {
415 json::Value j;
416 j[sfEmailHash] = "";
417 STParsedJSONObject obj("Test", j);
418 BEAST_EXPECT(obj.object.has_value());
419 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
420 BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash));
421 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
422 auto const& h128 = obj.object->getFieldH128(sfEmailHash);
423 BEAST_EXPECT(h128.size() == 16);
424 bool const allZero = std::ranges::all_of(h128, [](auto b) { return b == 0; });
425 BEAST_EXPECT(allZero);
426 }
427
428 // Odd-length hex string for UInt128 (should fail)
429 {
430 json::Value j;
431 j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDE";
432 STParsedJSONObject const obj("Test", j);
433 BEAST_EXPECT(!obj.object.has_value());
434 }
435
436 // Non-hex string for UInt128 (should fail)
437 {
438 json::Value j;
439 j[sfEmailHash] = "nothexstring";
440 STParsedJSONObject const obj("Test", j);
441 BEAST_EXPECT(!obj.object.has_value());
442 }
443
444 // Hex string too short for UInt128 (should fail)
445 {
446 json::Value j;
447 j[sfEmailHash] = "01234567";
448 STParsedJSONObject const obj("Test", j);
449 BEAST_EXPECT(!obj.object.has_value());
450 }
451
452 // Hex string too long for UInt128 (should fail)
453 {
454 json::Value j;
455 j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF00";
456 STParsedJSONObject const obj("Test", j);
457 BEAST_EXPECT(!obj.object.has_value());
458 }
459
460 // Array value for UInt128 (should fail)
461 {
462 json::Value j;
463 j[sfEmailHash] = json::Value(json::ValueType::Array);
464 STParsedJSONObject const obj("Test", j);
465 BEAST_EXPECT(!obj.object.has_value());
466 }
467
468 // Object value for UInt128 (should fail)
469 {
470 json::Value j;
471 j[sfEmailHash] = json::Value(json::ValueType::Object);
472 STParsedJSONObject const obj("Test", j);
473 BEAST_EXPECT(!obj.object.has_value());
474 }
475 }
476
477 void
479 {
480 testcase("UInt160");
481 {
482 json::Value j;
483 j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF01234567";
484 STParsedJSONObject obj("Test", j);
485 BEAST_EXPECT(obj.object.has_value());
486 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
487 BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency));
488 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
489 BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20);
490 std::array<uint8_t, 20> const expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD,
491 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
492 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67};
493 BEAST_EXPECT(
494 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
495 obj.object->getFieldH160(sfTakerPaysCurrency) == uint160::fromRaw(expected));
496 }
497 // Valid lowercase hex string for UInt160
498 {
499 json::Value j;
500 j[sfTakerPaysCurrency] = "0123456789abcdef0123456789abcdef01234567";
501 STParsedJSONObject obj("Test", j);
502 BEAST_EXPECT(obj.object.has_value());
503 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
504 BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency));
505 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
506 BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20);
507 }
508
509 // Empty string for UInt160 (should be valid, all zero)
510 {
511 json::Value j;
512 j[sfTakerPaysCurrency] = "";
513 STParsedJSONObject obj("Test", j);
514 BEAST_EXPECT(obj.object.has_value());
515 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
516 BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency));
517 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
518 auto const& h160 = obj.object->getFieldH160(sfTakerPaysCurrency);
519 BEAST_EXPECT(h160.size() == 20);
520 bool const allZero = std::ranges::all_of(h160, [](auto b) { return b == 0; });
521 BEAST_EXPECT(allZero);
522 }
523
524 // Non-hex string for UInt160 (should fail)
525 {
526 json::Value j;
527 j[sfTakerPaysCurrency] = "nothexstring";
528 STParsedJSONObject const obj("Test", j);
529 BEAST_EXPECT(!obj.object.has_value());
530 }
531
532 // Hex string too short for UInt160 (should fail)
533 {
534 json::Value j;
535 j[sfTakerPaysCurrency] = "01234567";
536 STParsedJSONObject const obj("Test", j);
537 BEAST_EXPECT(!obj.object.has_value());
538 }
539
540 // Hex string too long for UInt160 (should fail)
541 {
542 json::Value j;
543 j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF0123456789";
544 STParsedJSONObject const obj("Test", j);
545 BEAST_EXPECT(!obj.object.has_value());
546 }
547
548 // Array value for UInt160 (should fail)
549 {
550 json::Value j;
551 j[sfTakerPaysCurrency] = json::Value(json::ValueType::Array);
552 STParsedJSONObject const obj("Test", j);
553 BEAST_EXPECT(!obj.object.has_value());
554 }
555
556 // Object value for UInt160 (should fail)
557 {
558 json::Value j;
559 j[sfTakerPaysCurrency] = json::Value(json::ValueType::Object);
560 STParsedJSONObject const obj("Test", j);
561 BEAST_EXPECT(!obj.object.has_value());
562 }
563 }
564
565 void
567 {
568 testcase("UInt192");
569 {
570 json::Value j;
571 j[sfMPTokenIssuanceID] = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
572 STParsedJSONObject obj("Test", j);
573 BEAST_EXPECT(obj.object.has_value());
574 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
575 BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID));
576 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
577 BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24);
578 std::array<uint8_t, 24> const expected = {
579 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
580 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
581 BEAST_EXPECT(
582 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
583 obj.object->getFieldH192(sfMPTokenIssuanceID) == uint192::fromRaw(expected));
584 }
585
586 // Valid lowercase hex string for UInt192
587 {
588 json::Value j;
589 j[sfMPTokenIssuanceID] = "ffffffffffffffffffffffffffffffffffffffffffffffff";
590 STParsedJSONObject obj("Test", j);
591 BEAST_EXPECT(obj.object.has_value());
592 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
593 BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID));
594 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
595 BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24);
596 }
597
598 // Empty string for UInt192 (should be valid, all zero)
599 {
600 json::Value j;
601 j[sfMPTokenIssuanceID] = "";
602 STParsedJSONObject obj("Test", j);
603 BEAST_EXPECT(obj.object.has_value());
604 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
605 BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID));
606 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
607 auto const& h192 = obj.object->getFieldH192(sfMPTokenIssuanceID);
608 BEAST_EXPECT(h192.size() == 24);
609 bool const allZero = std::ranges::all_of(h192, [](auto b) { return b == 0; });
610 BEAST_EXPECT(allZero);
611 }
612
613 // Odd-length hex string for UInt192 (should fail)
614 {
615 json::Value j;
616 j[sfMPTokenIssuanceID] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDE";
617 STParsedJSONObject const obj("Test", j);
618 BEAST_EXPECT(!obj.object.has_value());
619 }
620
621 // Non-hex string for UInt192 (should fail)
622 {
623 json::Value j;
624 j[sfMPTokenIssuanceID] = "nothexstring";
625 STParsedJSONObject const obj("Test", j);
626 BEAST_EXPECT(!obj.object.has_value());
627 }
628
629 // Hex string too short for UInt192 (should fail)
630 {
631 json::Value j;
632 j[sfMPTokenIssuanceID] = "01234567";
633 STParsedJSONObject const obj("Test", j);
634 BEAST_EXPECT(!obj.object.has_value());
635 }
636
637 // Hex string too long for UInt192 (should fail)
638 {
639 json::Value j;
640 j[sfMPTokenIssuanceID] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF00";
641 STParsedJSONObject const obj("Test", j);
642 BEAST_EXPECT(!obj.object.has_value());
643 }
644
645 // Array value for UInt192 (should fail)
646 {
647 json::Value j;
648 j[sfMPTokenIssuanceID] = json::Value(json::ValueType::Array);
649 STParsedJSONObject const obj("Test", j);
650 BEAST_EXPECT(!obj.object.has_value());
651 }
652
653 // Object value for UInt192 (should fail)
654 {
655 json::Value j;
656 j[sfMPTokenIssuanceID] = json::Value(json::ValueType::Object);
657 STParsedJSONObject const obj("Test", j);
658 BEAST_EXPECT(!obj.object.has_value());
659 }
660 }
661
662 void
664 {
665 testcase("UInt256");
666 // Test with valid hex string for UInt256
667 {
668 json::Value j;
669 j[sfLedgerHash] =
670 "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD"
671 "EF";
672 STParsedJSONObject obj("Test", j);
673 BEAST_EXPECT(obj.object.has_value());
674 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
675 BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash));
676 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
677 BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32);
678 std::array<uint8_t, 32> const expected = {
679 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45,
680 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
681 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
682 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
683 BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash) == uint256::fromRaw(expected));
684 }
685 // Valid lowercase hex string for UInt256
686 {
687 json::Value j;
688 j[sfLedgerHash] =
689 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd"
690 "ef";
691 STParsedJSONObject obj("Test", j);
692 BEAST_EXPECT(obj.object.has_value());
693 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
694 BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash));
695 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
696 BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32);
697 }
698
699 // Empty string for UInt256 (should be valid, all zero)
700 {
701 json::Value j;
702 j[sfLedgerHash] = "";
703 STParsedJSONObject obj("Test", j);
704 BEAST_EXPECT(obj.object.has_value());
705 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
706 BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash));
707 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
708 auto const& h256 = obj.object->getFieldH256(sfLedgerHash);
709 BEAST_EXPECT(h256.size() == 32);
710 bool const allZero = std::ranges::all_of(h256, [](auto b) { return b == 0; });
711 BEAST_EXPECT(allZero);
712 }
713
714 // Odd-length hex string for UInt256 (should fail)
715 {
716 json::Value j;
717 j[sfLedgerHash] =
718 "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD"
719 "E";
720 STParsedJSONObject const obj("Test", j);
721 BEAST_EXPECT(!obj.object.has_value());
722 }
723
724 // Non-hex string for UInt256 (should fail)
725 {
726 json::Value j;
727 j[sfLedgerHash] = "nothexstring";
728 STParsedJSONObject const obj("Test", j);
729 BEAST_EXPECT(!obj.object.has_value());
730 }
731
732 // Hex string too short for UInt256 (should fail)
733 {
734 json::Value j;
735 j[sfLedgerHash] = "01234567";
736 STParsedJSONObject const obj("Test", j);
737 BEAST_EXPECT(!obj.object.has_value());
738 }
739
740 // Hex string too long for UInt256 (should fail)
741 {
742 json::Value j;
743 j[sfLedgerHash] =
744 "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD"
745 "EF00";
746 STParsedJSONObject const obj("Test", j);
747 BEAST_EXPECT(!obj.object.has_value());
748 }
749
750 // Array value for UInt256 (should fail)
751 {
752 json::Value j;
753 j[sfLedgerHash] = json::Value(json::ValueType::Array);
754 STParsedJSONObject const obj("Test", j);
755 BEAST_EXPECT(!obj.object.has_value());
756 }
757
758 // Object value for UInt256 (should fail)
759 {
760 json::Value j;
761 j[sfLedgerHash] = json::Value(json::ValueType::Object);
762 STParsedJSONObject const obj("Test", j);
763 BEAST_EXPECT(!obj.object.has_value());
764 }
765 }
766
767 void
769 {
770 testcase("Int32");
771 {
772 json::Value j;
773 int const minInt32 = -2147483648;
774 j[sfLoanScale] = minInt32;
775 STParsedJSONObject obj("Test", j);
776 BEAST_EXPECT(obj.object.has_value());
777 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
778 if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
779 {
780 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
781 BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == minInt32);
782 }
783 }
784
785 // max value
786 {
787 json::Value j;
788 int const maxInt32 = 2147483647;
789 j[sfLoanScale] = maxInt32;
790 STParsedJSONObject obj("Test", j);
791 BEAST_EXPECT(obj.object.has_value());
792 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
793 if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
794 {
795 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
796 BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == maxInt32);
797 }
798 }
799
800 // max uint value
801 {
802 json::Value j;
803 unsigned int const maxUInt32 = 2147483647u;
804 j[sfLoanScale] = maxUInt32;
805 STParsedJSONObject obj("Test", j);
806 BEAST_EXPECT(obj.object.has_value());
807 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
808 if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
809 {
810 BEAST_EXPECT(
811 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
812 obj.object->getFieldI32(sfLoanScale) == static_cast<int32_t>(maxUInt32));
813 }
814 }
815
816 // Test with string value
817 {
818 json::Value j;
819 j[sfLoanScale] = "2147483647";
820 STParsedJSONObject obj("Test", j);
821 BEAST_EXPECT(obj.object.has_value());
822 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
823 if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
824 {
825 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
826 BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == 2147483647u);
827 }
828 }
829
830 // Test with string negative value
831 {
832 json::Value j;
833 int const value = -2147483648;
834 j[sfLoanScale] = std::to_string(value);
835 STParsedJSONObject obj("Test", j);
836 BEAST_EXPECT(obj.object.has_value());
837 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
838 if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
839 {
840 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
841 BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == value);
842 }
843 }
844
845 // Test out of range value for int32 (negative)
846 {
847 json::Value j;
848 j[sfLoanScale] = "-2147483649";
849 STParsedJSONObject const obj("Test", j);
850 BEAST_EXPECT(!obj.object.has_value());
851 }
852
853 // Test out of range value for int32 (positive)
854 {
855 json::Value j;
856 j[sfLoanScale] = 2147483648u;
857 STParsedJSONObject const obj("Test", j);
858 BEAST_EXPECT(!obj.object.has_value());
859 }
860
861 // Test string value out of range
862 {
863 json::Value j;
864 j[sfLoanScale] = "2147483648";
865 STParsedJSONObject const obj("Test", j);
866 BEAST_EXPECT(!obj.object.has_value());
867 }
868
869 // Test bad_type (arrayValue)
870 {
871 json::Value j;
872 j[sfLoanScale] = json::Value(json::ValueType::Array);
873 STParsedJSONObject const obj("Test", j);
874 BEAST_EXPECT(!obj.object.has_value());
875 }
876
877 // Test bad_type (objectValue)
878 {
879 json::Value j;
880 j[sfLoanScale] = json::Value(json::ValueType::Object);
881 STParsedJSONObject const obj("Test", j);
882 BEAST_EXPECT(!obj.object.has_value());
883 }
884 }
885
886 void
888 {
889 testcase("Blob");
890 // Test with valid hex string for blob
891 {
892 json::Value j;
893 j[sfPublicKey] = "DEADBEEF";
894 STParsedJSONObject obj("Test", j);
895 BEAST_EXPECT(obj.object.has_value());
896 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
897 BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey));
898 auto const& blob =
899 obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access)
900 BEAST_EXPECT(blob.size() == 4);
901 BEAST_EXPECT(blob[0] == 0xDE);
902 BEAST_EXPECT(blob[1] == 0xAD);
903 BEAST_EXPECT(blob[2] == 0xBE);
904 BEAST_EXPECT(blob[3] == 0xEF);
905 }
906
907 // Test empty string for blob (should be valid, size 0)
908 {
909 json::Value j;
910 j[sfPublicKey] = "";
911 STParsedJSONObject obj("Test", j);
912 BEAST_EXPECT(obj.object.has_value());
913 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
914 BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey));
915 auto const& blob =
916 obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access)
917 BEAST_EXPECT(blob.empty());
918 }
919
920 // Test lowercase hex string for blob
921 {
922 json::Value j;
923 j[sfPublicKey] = "deadbeef";
924 STParsedJSONObject obj("Test", j);
925 BEAST_EXPECT(obj.object.has_value());
926 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
927 BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey));
928 auto const& blob =
929 obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access)
930 BEAST_EXPECT(blob.size() == 4);
931 BEAST_EXPECT(blob[0] == 0xDE);
932 BEAST_EXPECT(blob[1] == 0xAD);
933 BEAST_EXPECT(blob[2] == 0xBE);
934 BEAST_EXPECT(blob[3] == 0xEF);
935 }
936
937 // Test non-hex string for blob (should fail)
938 {
939 json::Value j;
940 j[sfPublicKey] = "XYZ123";
941 STParsedJSONObject const obj("Test", j);
942 BEAST_EXPECT(!obj.object.has_value());
943 }
944
945 // Test array value for blob (should fail)
946 {
947 json::Value j;
948 j[sfPublicKey] = json::Value(json::ValueType::Array);
949 STParsedJSONObject const obj("Test", j);
950 BEAST_EXPECT(!obj.object.has_value());
951 }
952
953 // Test object value for blob (should fail)
954 {
955 json::Value j;
956 j[sfPublicKey] = json::Value(json::ValueType::Object);
957 STParsedJSONObject const obj("Test", j);
958 BEAST_EXPECT(!obj.object.has_value());
959 }
960 }
961
962 void
964 {
965 testcase("Vector256");
966 // Test with valid array of hex strings for Vector256
967 {
968 json::Value j;
970 arr.append(
971 "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD"
972 "EF");
973 arr.append(
974 "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA98765432"
975 "10");
976 j[sfHashes] = arr;
977 STParsedJSONObject obj("Test", j);
978 BEAST_EXPECT(obj.object.has_value());
979 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
980 BEAST_EXPECT(obj.object->isFieldPresent(sfHashes));
981 auto const& vec =
982 obj.object->getFieldV256(sfHashes); // NOLINT(bugprone-unchecked-optional-access)
983 BEAST_EXPECT(vec.size() == 2);
984 BEAST_EXPECT(to_string(vec[0]) == arr[0u].asString());
985 BEAST_EXPECT(to_string(vec[1]) == arr[1u].asString());
986 }
987 // Test empty array for Vector256 (should be valid, size 0)
988 {
989 json::Value j;
991 j[sfHashes] = arr;
992 STParsedJSONObject obj("Test", j);
993 BEAST_EXPECT(obj.object.has_value());
994 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
995 BEAST_EXPECT(obj.object->isFieldPresent(sfHashes));
996 auto const& vec =
997 obj.object->getFieldV256(sfHashes); // NOLINT(bugprone-unchecked-optional-access)
998 BEAST_EXPECT(vec.empty());
999 }
1000
1001 // Test array with invalid hex string (should fail)
1002 {
1003 json::Value j;
1005 arr.append("nothexstring");
1006 j[sfHashes] = arr;
1007 STParsedJSONObject const obj("Test", j);
1008 BEAST_EXPECT(!obj.object.has_value());
1009 }
1010
1011 // Test array with string of wrong length (should fail)
1012 {
1013 json::Value j;
1015 arr.append("0123456789ABCDEF"); // too short for uint256
1016 j[sfHashes] = arr;
1017 STParsedJSONObject const obj("Test", j);
1018 BEAST_EXPECT(!obj.object.has_value());
1019 }
1020
1021 // Test array with non-string element (should fail)
1022 {
1023 json::Value j;
1025 arr.append(12345);
1026 j[sfHashes] = arr;
1027 STParsedJSONObject const obj("Test", j);
1028 BEAST_EXPECT(!obj.object.has_value());
1029 }
1030
1031 // Test non-array value for Vector256 (should fail)
1032 {
1033 json::Value j;
1034 j[sfHashes] = "notanarray";
1035 STParsedJSONObject const obj("Test", j);
1036 BEAST_EXPECT(!obj.object.has_value());
1037 }
1038
1039 // Test array with object element (should fail)
1040 {
1041 json::Value j;
1044 objElem["foo"] = "bar";
1045 arr.append(objElem);
1046 j[sfHashes] = arr;
1047 STParsedJSONObject const obj("Test", j);
1048 BEAST_EXPECT(!obj.object.has_value());
1049 }
1050 }
1051
1052 void
1054 {
1055 testcase("Account");
1056 // Test with valid base58 string for AccountID
1057 {
1058 json::Value j;
1059 j[sfAccount] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1060 STParsedJSONObject obj("Test", j);
1061 BEAST_EXPECT(obj.object.has_value());
1062 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1063 BEAST_EXPECT(obj.object->isFieldPresent(sfAccount));
1064 auto const& acct =
1065 obj.object->getAccountID(sfAccount); // NOLINT(bugprone-unchecked-optional-access)
1066 BEAST_EXPECT(acct.size() == 20);
1067 BEAST_EXPECT(toBase58(acct) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
1068 }
1069
1070 // Valid hex string for AccountID
1071 {
1072 json::Value j;
1073 j[sfAccount] = "000102030405060708090A0B0C0D0E0F10111213";
1074 STParsedJSONObject obj("Test", j);
1075 BEAST_EXPECT(obj.object.has_value());
1076 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1077 BEAST_EXPECT(obj.object->isFieldPresent(sfAccount));
1078 auto const& acct =
1079 obj.object->getAccountID(sfAccount); // NOLINT(bugprone-unchecked-optional-access)
1080 BEAST_EXPECT(acct.size() == 20);
1081 }
1082
1083 // Invalid base58 string for AccountID
1084 {
1085 json::Value j;
1086 j[sfAccount] = "notAValidBase58Account";
1087 STParsedJSONObject const obj("Test", j);
1088 BEAST_EXPECT(!obj.object.has_value());
1089 }
1090
1091 // Invalid hex string for AccountID (too short)
1092 {
1093 json::Value j;
1094 j[sfAccount] = "001122334455";
1095 STParsedJSONObject const obj("Test", j);
1096 BEAST_EXPECT(!obj.object.has_value());
1097 }
1098
1099 // Invalid hex string for AccountID (too long)
1100 {
1101 json::Value j;
1102 j[sfAccount] = "000102030405060708090A0B0C0D0E0F101112131415";
1103 STParsedJSONObject const obj("Test", j);
1104 BEAST_EXPECT(!obj.object.has_value());
1105 }
1106
1107 // Invalid hex string for AccountID (bad chars)
1108 {
1109 json::Value j;
1110 j[sfAccount] = "000102030405060708090A0B0C0D0E0F1011121G";
1111 STParsedJSONObject const obj("Test", j);
1112 BEAST_EXPECT(!obj.object.has_value());
1113 }
1114
1115 // Empty string for AccountID (should fail)
1116 {
1117 json::Value j;
1118 j[sfAccount] = "";
1119 STParsedJSONObject const obj("Test", j);
1120 BEAST_EXPECT(!obj.object.has_value());
1121 }
1122
1123 // Array value for AccountID (should fail)
1124 {
1125 json::Value j;
1126 j[sfAccount] = json::Value(json::ValueType::Array);
1127 STParsedJSONObject const obj("Test", j);
1128 BEAST_EXPECT(!obj.object.has_value());
1129 }
1130
1131 // Object value for AccountID (should fail)
1132 {
1133 json::Value j;
1134 j[sfAccount] = json::Value(json::ValueType::Object);
1135 STParsedJSONObject const obj("Test", j);
1136 BEAST_EXPECT(!obj.object.has_value());
1137 }
1138 }
1139
1140 void
1142 {
1143 testcase("Currency");
1144 // Test with valid ISO code for currency
1145 {
1146 json::Value j;
1147 j[sfBaseAsset] = "USD";
1148 STParsedJSONObject obj("Test", j);
1149 BEAST_EXPECT(obj.object.has_value());
1150 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1151 BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
1152 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1153 auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
1154 BEAST_EXPECT(curr.currency().size() == 20);
1155 }
1156
1157 // Valid ISO code
1158 {
1159 json::Value j;
1160 j[sfBaseAsset] = "EUR";
1161 STParsedJSONObject obj("Test", j);
1162 BEAST_EXPECT(obj.object.has_value());
1163 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1164 BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
1165 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1166 auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
1167 BEAST_EXPECT(curr.currency().size() == 20);
1168 }
1169
1170 // Valid hex string for currency
1171 {
1172 json::Value j;
1173 j[sfBaseAsset] = "0123456789ABCDEF01230123456789ABCDEF0123";
1174 STParsedJSONObject obj("Test", j);
1175 if (BEAST_EXPECT(obj.object); obj.object.has_value())
1176 {
1177 BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
1178 auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
1179 BEAST_EXPECT(curr.currency().size() == 20);
1180 }
1181 }
1182
1183 // Invalid ISO code (too long)
1184 {
1185 json::Value j;
1186 j[sfBaseAsset] = "USDD";
1187 STParsedJSONObject const obj("Test", j);
1188 BEAST_EXPECT(!obj.object.has_value());
1189 }
1190
1191 // lowercase ISO code
1192 {
1193 json::Value j;
1194 j[sfBaseAsset] = "usd";
1195 STParsedJSONObject obj("Test", j);
1196 BEAST_EXPECT(obj.object.has_value());
1197 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1198 BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
1199 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1200 auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
1201 BEAST_EXPECT(curr.currency().size() == 20);
1202 }
1203
1204 // Invalid hex string (too short)
1205 {
1206 json::Value j;
1207 j[sfBaseAsset] = "0123456789AB";
1208 STParsedJSONObject const obj("Test", j);
1209 BEAST_EXPECT(!obj.object.has_value());
1210 }
1211
1212 // Invalid hex string (too long)
1213 {
1214 json::Value j;
1215 j[sfBaseAsset] = "0123456789ABCDEF0123456789";
1216 STParsedJSONObject const obj("Test", j);
1217 BEAST_EXPECT(!obj.object.has_value());
1218 }
1219
1220 // Empty string for currency (should fail)
1221 {
1222 json::Value j;
1223 j[sfBaseAsset] = "";
1224 STParsedJSONObject obj("Test", j);
1225 BEAST_EXPECT(obj.object.has_value());
1226 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1227 BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
1228 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1229 auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
1230 BEAST_EXPECT(curr.currency().size() == 20);
1231 }
1232
1233 // Array value for currency (should fail)
1234 {
1235 json::Value j;
1236 j[sfBaseAsset] = json::Value(json::ValueType::Array);
1237 STParsedJSONObject const obj("Test", j);
1238 BEAST_EXPECT(!obj.object.has_value());
1239 }
1240
1241 // Object value for currency (should fail)
1242 {
1243 json::Value j;
1244 j[sfBaseAsset] = json::Value(json::ValueType::Object);
1245 STParsedJSONObject const obj("Test", j);
1246 BEAST_EXPECT(!obj.object.has_value());
1247 }
1248 }
1249
1250 void
1252 {
1253 testcase("Amount");
1254 // Test with string value for Amount
1255 {
1256 json::Value j;
1257 j[sfAmount] = "100000000000000000";
1258 STParsedJSONObject obj("Test", j);
1259 BEAST_EXPECT(obj.object.has_value());
1260 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1261 BEAST_EXPECT(obj.object->isFieldPresent(sfAmount));
1262 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1263 BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(100000000000000000ull));
1264 }
1265
1266 // Test with int value for Amount
1267 {
1268 json::Value j;
1269 j[sfAmount] = 4294967295u;
1270 STParsedJSONObject obj("Test", j);
1271 BEAST_EXPECT(obj.object.has_value());
1272 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1273 BEAST_EXPECT(obj.object->isFieldPresent(sfAmount));
1274 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1275 BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(4294967295u));
1276 }
1277
1278 // Test with decimal string for Amount (should fail)
1279 {
1280 json::Value j;
1281 j[sfAmount] = "123.45";
1282 STParsedJSONObject const obj("Test", j);
1283 BEAST_EXPECT(!obj.object.has_value());
1284 }
1285
1286 // Test with empty string for Amount (should fail)
1287 {
1288 json::Value j;
1289 j[sfAmount] = "";
1290 STParsedJSONObject const obj("Test", j);
1291 BEAST_EXPECT(!obj.object.has_value());
1292 }
1293
1294 // Test with non-numeric string for Amount (should fail)
1295 {
1296 json::Value j;
1297 j[sfAmount] = "notanumber";
1298 STParsedJSONObject const obj("Test", j);
1299 BEAST_EXPECT(!obj.object.has_value());
1300 }
1301
1302 // Test with object value for Amount (should fail)
1303 {
1304 json::Value j;
1305 j[sfAmount] = json::Value(json::ValueType::Object);
1306 STParsedJSONObject const obj("Test", j);
1307 BEAST_EXPECT(!obj.object.has_value());
1308 }
1309 }
1310
1311 void
1313 {
1314 testcase("PathSet");
1315 // Valid test: single path with single element
1316 {
1317 json::Value j;
1320 elem["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1321 elem["currency"] = "USD";
1322 elem["issuer"] = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe";
1323 path.append(elem);
1325 pathset.append(path);
1326 j[sfPaths] = pathset;
1327 STParsedJSONObject obj("Test", j);
1328 if (BEAST_EXPECT(obj.object.has_value()))
1329 {
1330 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1331 BEAST_EXPECT(obj.object->isFieldPresent(sfPaths));
1332 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1333 auto const& ps = obj.object->getFieldPathSet(sfPaths);
1334 BEAST_EXPECT(!ps.empty());
1335 BEAST_EXPECT(ps.size() == 1);
1336 BEAST_EXPECT(ps[0].size() == 1);
1337 BEAST_EXPECT(
1338 ps[0][0].getAccountID() ==
1339 parseBase58<AccountID>("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"));
1340 BEAST_EXPECT(to_string(ps[0][0].getCurrency()) == "USD");
1341 BEAST_EXPECT(
1342 ps[0][0].getIssuerID() ==
1343 parseBase58<AccountID>("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"));
1344 }
1345 }
1346
1347 // Valid test: non-standard currency code
1348 {
1349 json::Value j;
1352 elem["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1353 elem["currency"] = "0123456789ABCDEF01230123456789ABCDEF0123";
1354 elem["issuer"] = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe";
1355 path.append(elem);
1357 pathset.append(path);
1358 j[sfPaths] = pathset;
1359 STParsedJSONObject obj("Test", j);
1360 BEAST_EXPECT(obj.object.has_value());
1361 BEAST_EXPECT(
1362 obj.object->isFieldPresent(sfPaths)); // NOLINT(bugprone-unchecked-optional-access)
1363 auto const& ps =
1364 obj.object->getFieldPathSet(sfPaths); // NOLINT(bugprone-unchecked-optional-access)
1365 BEAST_EXPECT(!ps.empty());
1366 }
1367
1368 // Test with non-array value for PathSet (should fail)
1369 {
1370 json::Value j;
1371 j[sfPaths] = "notanarray";
1372 STParsedJSONObject const obj("Test", j);
1373 BEAST_EXPECT(!obj.object.has_value());
1374 }
1375
1376 // Test with array containing non-array element (should fail)
1377 {
1378 json::Value j;
1380 pathset.append("notanarray");
1381 j[sfPaths] = pathset;
1382 STParsedJSONObject const obj("Test", j);
1383 BEAST_EXPECT(!obj.object.has_value());
1384 }
1385
1386 // Test with array containing array with non-object element (should
1387 // fail)
1388 {
1389 json::Value j;
1391 path.append("notanobject");
1393 pathset.append(path);
1394 j[sfPaths] = pathset;
1395 STParsedJSONObject const obj("Test", j);
1396 BEAST_EXPECT(!obj.object.has_value());
1397 }
1398
1399 // Test with array containing array with object missing required keys
1400 // (should fail)
1401 {
1402 json::Value j;
1405 elem["foo"] = "bar"; // not a valid path element key
1406 path.append(elem);
1408 pathset.append(path);
1409 j[sfPaths] = pathset;
1410 STParsedJSONObject const obj("Test", j);
1411 BEAST_EXPECT(!obj.object.has_value());
1412 }
1413
1414 // Test with array containing array with object with invalid account
1415 // value (should fail)
1416 {
1417 json::Value j;
1420 elem["account"] = "notAValidBase58Account";
1421 path.append(elem);
1423 pathset.append(path);
1424 j[sfPaths] = pathset;
1425 STParsedJSONObject const obj("Test", j);
1426 BEAST_EXPECT(!obj.object.has_value());
1427 }
1428
1429 // Test with account not string (should fail)
1430 {
1431 json::Value j;
1434 elem["account"] = 12345;
1435 path.append(elem);
1437 pathset.append(path);
1438 j[sfPaths] = pathset;
1439 STParsedJSONObject const obj("Test", j);
1440 BEAST_EXPECT(!obj.object.has_value());
1441 }
1442
1443 // Test with currency not string (should fail)
1444 {
1445 json::Value j;
1448 elem["currency"] = 12345;
1449 path.append(elem);
1451 pathset.append(path);
1452 j[sfPaths] = pathset;
1453 STParsedJSONObject const obj("Test", j);
1454 BEAST_EXPECT(!obj.object.has_value());
1455 }
1456
1457 // Test with non-standard currency not hex (should fail)
1458 {
1459 json::Value j;
1462 elem["currency"] = "notAValidCurrency";
1463 path.append(elem);
1465 pathset.append(path);
1466 j[sfPaths] = pathset;
1467 STParsedJSONObject const obj("Test", j);
1468 BEAST_EXPECT(!obj.object.has_value());
1469 }
1470
1471 // Test with issuer not string (should fail)
1472 {
1473 json::Value j;
1476 elem["issuer"] = 12345;
1477 path.append(elem);
1479 pathset.append(path);
1480 j[sfPaths] = pathset;
1481 STParsedJSONObject const obj("Test", j);
1482 BEAST_EXPECT(!obj.object.has_value());
1483 }
1484
1485 // Test with issuer not base58 (should fail)
1486 {
1487 json::Value j;
1490 elem["issuer"] = "notAValidBase58Account";
1491 path.append(elem);
1493 pathset.append(path);
1494 j[sfPaths] = pathset;
1495 STParsedJSONObject const obj("Test", j);
1496 BEAST_EXPECT(!obj.object.has_value());
1497 }
1498 }
1499
1500 void
1502 {
1503 testcase("Issue");
1504 // Valid Issue: currency and issuer as base58
1505 {
1506 json::Value j;
1508 issueJson["currency"] = "USD";
1509 issueJson["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1510 j[sfAsset] = issueJson;
1511 STParsedJSONObject obj("Test", j);
1512 if (BEAST_EXPECT(obj.object.has_value()))
1513 {
1514 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1515 BEAST_EXPECT(obj.object->isFieldPresent(sfAsset));
1516 auto const& issueField =
1517 (*obj.object)[sfAsset]; // NOLINT(bugprone-unchecked-optional-access)
1518 auto const issue = issueField.value().get<Issue>();
1519 BEAST_EXPECT(issue.currency.size() == 20);
1520 BEAST_EXPECT(to_string(issue.currency) == "USD");
1521 BEAST_EXPECT(issue.account.size() == 20);
1522 BEAST_EXPECT(
1523 issue.account == parseBase58<AccountID>("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"));
1524 }
1525 }
1526
1527 // Valid Issue: currency as hex
1528 {
1529 json::Value j;
1531 issueJson["currency"] = "0123456789ABCDEF01230123456789ABCDEF0123";
1532 issueJson["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1533 j[sfAsset] = issueJson;
1534 STParsedJSONObject obj("Test", j);
1535 if (BEAST_EXPECT(obj.object); obj.object.has_value())
1536 {
1537 BEAST_EXPECT(obj.object->isFieldPresent(sfAsset));
1538 auto const& issueField = (*obj.object)[sfAsset];
1539 auto const issue = issueField.value().get<Issue>();
1540 BEAST_EXPECT(issue.currency.size() == 20);
1541 BEAST_EXPECT(issue.account.size() == 20);
1542 }
1543 }
1544
1545 // Valid Issue: MPTID
1546 {
1547 json::Value j;
1549 issueJson["mpt_issuance_id"] = "0000000000000000000000004D5054494431323334234234";
1550 j[sfAsset] = issueJson;
1551 STParsedJSONObject obj("Test", j);
1552 if (BEAST_EXPECT(obj.object); obj.object.has_value())
1553 {
1554 BEAST_EXPECT(obj.object->isFieldPresent(sfAsset));
1555 auto const& issueField = (*obj.object)[sfAsset];
1556 auto const issue = issueField.value().get<MPTIssue>();
1557 BEAST_EXPECT(issue.getMptID().size() == 24);
1558 }
1559 }
1560
1561 // Invalid Issue: missing currency
1562 {
1563 json::Value j;
1565 issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1566 j[sfAsset] = issue;
1567 STParsedJSONObject const obj("Test", j);
1568 BEAST_EXPECT(!obj.object.has_value());
1569 }
1570
1571 // Invalid Issue: missing issuer
1572 {
1573 json::Value j;
1575 issue["currency"] = "USD";
1576 j[sfAsset] = issue;
1577 STParsedJSONObject const obj("Test", j);
1578 BEAST_EXPECT(!obj.object.has_value());
1579 }
1580
1581 // Invalid Issue: currency too long
1582 {
1583 json::Value j;
1585 issue["currency"] = "USDD";
1586 issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1587 j[sfAsset] = issue;
1588 STParsedJSONObject const obj("Test", j);
1589 BEAST_EXPECT(!obj.object.has_value());
1590 }
1591
1592 // Invalid Issue: issuer not base58 or hex
1593 {
1594 json::Value j;
1596 issue["currency"] = "USD";
1597 issue["issuer"] = "notAValidIssuer";
1598 j[sfAsset] = issue;
1599 STParsedJSONObject const obj("Test", j);
1600 BEAST_EXPECT(!obj.object.has_value());
1601 }
1602
1603 // Invalid Issue: currency not string
1604 {
1605 json::Value j;
1607 issue["currency"] = json::Value(json::ValueType::Array);
1608 issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1609 j[sfAsset] = issue;
1610 STParsedJSONObject const obj("Test", j);
1611 BEAST_EXPECT(!obj.object.has_value());
1612 }
1613
1614 // Invalid Issue: issuer not string
1615 {
1616 json::Value j;
1618 issue["currency"] = "USD";
1619 issue["issuer"] = json::Value(json::ValueType::Object);
1620 j[sfAsset] = issue;
1621 STParsedJSONObject const obj("Test", j);
1622 BEAST_EXPECT(!obj.object.has_value());
1623 }
1624
1625 // Invalid Issue: not an object
1626 {
1627 json::Value j;
1628 j[sfAsset] = "notanobject";
1629 STParsedJSONObject const obj("Test", j);
1630 BEAST_EXPECT(!obj.object.has_value());
1631 }
1632 }
1633
1634 void
1636 {
1637 testcase("XChainBridge");
1638 // Valid XChainBridge
1639 {
1640 json::Value j;
1642 json::Value issuingChainIssue(json::ValueType::Object);
1643 issuingChainIssue["currency"] = "USD";
1644 issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1645 json::Value lockingChainIssue(json::ValueType::Object);
1646 lockingChainIssue["currency"] = "EUR";
1647 lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1648 bridge["LockingChainIssue"] = lockingChainIssue;
1649 bridge["IssuingChainIssue"] = issuingChainIssue;
1650 bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1651 bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1652 j[sfXChainBridge] = bridge;
1653 STParsedJSONObject obj("Test", j);
1654 if (BEAST_EXPECT(obj.object); obj.object.has_value())
1655 {
1656 BEAST_EXPECT(obj.object->isFieldPresent(sfXChainBridge));
1657 auto const& bridgeField = (*obj.object)[sfXChainBridge];
1658 BEAST_EXPECT(bridgeField->lockingChainIssue().currency.size() == 20);
1659 BEAST_EXPECT(bridgeField->issuingChainIssue().currency.size() == 20);
1660 }
1661 }
1662
1663 // Valid XChainBridge: issues as hex currency
1664 {
1665 json::Value j;
1667 json::Value issuingChainIssue(json::ValueType::Object);
1668 issuingChainIssue["currency"] = "0123456789ABCDEF01230123456789ABCDEF0123";
1669 issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1670 json::Value lockingChainIssue(json::ValueType::Object);
1671 lockingChainIssue["currency"] = "0123456789ABCDEF01230123456789ABCDEF0123";
1672 lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1673 bridge["LockingChainIssue"] = lockingChainIssue;
1674 bridge["IssuingChainIssue"] = issuingChainIssue;
1675 bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1676 bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1677 j[sfXChainBridge] = bridge;
1678 STParsedJSONObject obj("Test", j);
1679 if (BEAST_EXPECT(obj.object); obj.object.has_value())
1680 {
1681 BEAST_EXPECT(obj.object->isFieldPresent(sfXChainBridge));
1682 auto const& bridgeField = (*obj.object)[sfXChainBridge];
1683 BEAST_EXPECT(bridgeField->lockingChainIssue().currency.size() == 20);
1684 BEAST_EXPECT(bridgeField->issuingChainIssue().currency.size() == 20);
1685 }
1686 }
1687
1688 // Invalid XChainBridge: missing LockingChainIssue
1689 {
1690 json::Value j;
1692 json::Value issuingChainIssue(json::ValueType::Object);
1693 issuingChainIssue["currency"] = "USD";
1694 issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1695 bridge["IssuingChainIssue"] = issuingChainIssue;
1696 bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1697 bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1698 j[sfXChainBridge] = bridge;
1699 STParsedJSONObject const obj("Test", j);
1700 BEAST_EXPECT(!obj.object.has_value());
1701 }
1702
1703 // Invalid XChainBridge: missing IssuingChainIssue
1704 {
1705 json::Value j;
1707 json::Value lockingChainIssue(json::ValueType::Object);
1708 lockingChainIssue["currency"] = "EUR";
1709 lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1710 bridge["LockingChainIssue"] = lockingChainIssue;
1711 bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1712 bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1713 j[sfXChainBridge] = bridge;
1714 STParsedJSONObject const obj("Test", j);
1715 BEAST_EXPECT(!obj.object.has_value());
1716 }
1717
1718 // Invalid XChainBridge: missing LockingChainDoor
1719 {
1720 json::Value j;
1722 json::Value issuingChainIssue(json::ValueType::Object);
1723 issuingChainIssue["currency"] = "USD";
1724 issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1725 bridge["IssuingChainIssue"] = issuingChainIssue;
1726 json::Value lockingChainIssue(json::ValueType::Object);
1727 lockingChainIssue["currency"] = "EUR";
1728 lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1729 bridge["LockingChainIssue"] = lockingChainIssue;
1730 bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1731 j[sfXChainBridge] = bridge;
1732 STParsedJSONObject const obj("Test", j);
1733 BEAST_EXPECT(!obj.object.has_value());
1734 }
1735
1736 // Invalid XChainBridge: missing IssuingChainDoor
1737 {
1738 json::Value j;
1740 json::Value issuingChainIssue(json::ValueType::Object);
1741 issuingChainIssue["currency"] = "USD";
1742 issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1743 bridge["IssuingChainIssue"] = issuingChainIssue;
1744 json::Value lockingChainIssue(json::ValueType::Object);
1745 lockingChainIssue["currency"] = "EUR";
1746 lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1747 bridge["LockingChainIssue"] = lockingChainIssue;
1748 bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1749 j[sfXChainBridge] = bridge;
1750 STParsedJSONObject const obj("Test", j);
1751 BEAST_EXPECT(!obj.object.has_value());
1752 }
1753
1754 // Invalid XChainBridge: IssuingChainIssue not an object
1755 {
1756 json::Value j;
1758 bridge["LockingChainIssue"] = "notanobject";
1759 bridge["IssuingChainIssue"] = "notanobject";
1760 j[sfXChainBridge] = bridge;
1761 STParsedJSONObject const obj("Test", j);
1762 BEAST_EXPECT(!obj.object.has_value());
1763 }
1764
1765 // Invalid XChainBridge: IssuingChainIssue missing currency
1766 {
1767 json::Value j;
1770 asset["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1771 json::Value lockingChainIssue(json::ValueType::Object);
1772 lockingChainIssue["currency"] = "EUR";
1773 lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1774 bridge["LockingChainIssue"] = lockingChainIssue;
1775 bridge["IssuingChainIssue"] = asset;
1776 j[sfXChainBridge] = bridge;
1777 STParsedJSONObject const obj("Test", j);
1778 BEAST_EXPECT(!obj.object.has_value());
1779 }
1780
1781 // Invalid XChainBridge: asset missing issuer
1782 {
1783 json::Value j;
1786 asset["currency"] = "USD";
1787 json::Value lockingChainIssue(json::ValueType::Object);
1788 lockingChainIssue["currency"] = "EUR";
1789 lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1790 bridge["LockingChainIssue"] = lockingChainIssue;
1791 bridge["IssuingChainIssue"] = asset;
1792 j[sfXChainBridge] = bridge;
1793 STParsedJSONObject const obj("Test", j);
1794 BEAST_EXPECT(!obj.object.has_value());
1795 }
1796
1797 // Invalid XChainBridge: asset issuer not base58
1798 {
1799 json::Value j;
1802 asset["currency"] = "USD";
1803 asset["issuer"] = "notAValidBase58Account";
1804 json::Value lockingChainIssue(json::ValueType::Object);
1805 lockingChainIssue["currency"] = "EUR";
1806 lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1807 bridge["LockingChainIssue"] = lockingChainIssue;
1808 bridge["IssuingChainIssue"] = asset;
1809 j[sfXChainBridge] = bridge;
1810 STParsedJSONObject const obj("Test", j);
1811 BEAST_EXPECT(!obj.object.has_value());
1812 }
1813
1814 // Invalid XChainBridge: not an object
1815 {
1816 json::Value j;
1817 j[sfXChainBridge] = "notanobject";
1818 STParsedJSONObject const obj("Test", j);
1819 BEAST_EXPECT(!obj.object.has_value());
1820 }
1821 }
1822
1823 void
1825 {
1826 testcase("Number");
1827 // Valid integer value for STNumber
1828 {
1829 json::Value j;
1830 j[sfNumber] = 12345;
1831 STParsedJSONObject obj("Test", j);
1832 BEAST_EXPECT(obj.object.has_value());
1833 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1834 BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
1835 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1836 BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(12345, 0));
1837 }
1838
1839 // Valid uint value for STNumber
1840 {
1841 json::Value j;
1842 j[sfNumber] = 12345u;
1843 STParsedJSONObject obj("Test", j);
1844 BEAST_EXPECT(obj.object.has_value());
1845 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1846 BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
1847 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1848 BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(12345, 0));
1849 }
1850
1851 // Valid string integer value for STNumber
1852 {
1853 json::Value j;
1854 j[sfNumber] = "67890";
1855 STParsedJSONObject obj("Test", j);
1856 BEAST_EXPECT(obj.object.has_value());
1857 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1858 BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
1859 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1860 BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(67890, 0));
1861 }
1862
1863 // Valid negative integer value for STNumber
1864 {
1865 json::Value j;
1866 j[sfNumber] = -42;
1867 STParsedJSONObject obj("Test", j);
1868 BEAST_EXPECT(obj.object.has_value());
1869 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1870 BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
1871 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1872 BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(-42, 0));
1873 }
1874
1875 // Valid string negative integer value for STNumber
1876 {
1877 json::Value j;
1878 j[sfNumber] = "-123";
1879 STParsedJSONObject obj("Test", j);
1880 BEAST_EXPECT(obj.object.has_value());
1881 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1882 BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
1883 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1884 BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(-123, 0));
1885 }
1886
1887 // Valid floating point value for STNumber
1888 {
1889 json::Value j;
1890 j[sfNumber] = "3.14159";
1891 STParsedJSONObject obj("Test", j);
1892 if (BEAST_EXPECT(obj.object); obj.object.has_value())
1893 {
1894 BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
1895 BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(314159, -5));
1896 }
1897 }
1898
1899 // Invalid string value for STNumber (not a number)
1900 {
1901 json::Value j;
1902 j[sfNumber] = "notanumber";
1903 STParsedJSONObject const obj("Test", j);
1904 BEAST_EXPECT(!obj.object.has_value());
1905 }
1906
1907 // Invalid array value for STNumber
1908 {
1909 json::Value j;
1910 j[sfNumber] = json::Value(json::ValueType::Array);
1911 STParsedJSONObject const obj("Test", j);
1912 BEAST_EXPECT(!obj.object.has_value());
1913 }
1914
1915 // Invalid object value for STNumber
1916 {
1917 json::Value j;
1918 j[sfNumber] = json::Value(json::ValueType::Object);
1919 STParsedJSONObject const obj("Test", j);
1920 BEAST_EXPECT(!obj.object.has_value());
1921 }
1922
1923 // Empty string for STNumber (should fail)
1924 {
1925 json::Value j;
1926 j[sfNumber] = "";
1927 STParsedJSONObject const obj("Test", j);
1928 BEAST_EXPECT(!obj.object.has_value());
1929 }
1930 }
1931
1932 void
1934 {
1935 testcase("Object");
1936 // Test with valid object for Object
1937 {
1938 json::Value j;
1940 objVal[sfTransactionResult] = 1;
1941 j[sfTransactionMetaData] = objVal;
1942 STParsedJSONObject obj("Test", j);
1943 BEAST_EXPECT(obj.object.has_value());
1944 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1945 BEAST_EXPECT(obj.object->isFieldPresent(sfTransactionMetaData));
1946 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1947 auto const& result = obj.object->peekFieldObject(sfTransactionMetaData);
1948 BEAST_EXPECT(result.getFieldU8(sfTransactionResult) == 1);
1949 }
1950
1951 // Test with non-object value for Object (should fail)
1952 {
1953 json::Value j;
1954 j[sfTransactionMetaData] = "notanobject";
1955 STParsedJSONObject const obj("Test", j);
1956 BEAST_EXPECT(!obj.object.has_value());
1957 }
1958
1959 // Test with array value for Object (should fail)
1960 {
1961 json::Value j;
1963 arr.append(1);
1964 j[sfTransactionMetaData] = arr;
1965 STParsedJSONObject const obj("Test", j);
1966 BEAST_EXPECT(!obj.object.has_value());
1967 }
1968
1969 // Test with null value for Object (should fail)
1970 {
1971 json::Value j;
1972 j[sfTransactionMetaData] = json::Value(json::ValueType::Null);
1973 STParsedJSONObject const obj("Test", j);
1974 BEAST_EXPECT(!obj.object.has_value());
1975 }
1976
1977 // Test with max depth (should succeed)
1978 // max depth is 64
1979 {
1980 json::Value j;
1982 json::Value* current = &obj;
1983 for (std::size_t i = 1; i < kMaxParsedJsonDepth; ++i)
1984 {
1986 (*current)[sfTransactionMetaData] = next;
1987 current = &((*current)[sfTransactionMetaData]);
1988 }
1989 (*current)[sfTransactionResult.getJsonName()] = 1;
1990 j[sfTransactionMetaData] = obj;
1991 STParsedJSONObject parsed("Test", j);
1992 BEAST_EXPECT(parsed.object.has_value());
1993 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1994 BEAST_EXPECT(parsed.object->isFieldPresent(sfTransactionMetaData));
1995 }
1996
1997 // Test with depth exceeding maxDepth (should fail)
1998 {
1999 json::Value j;
2001 json::Value* current = &obj;
2002 for (std::size_t i = 1; i <= kMaxParsedJsonDepth; ++i)
2003 {
2005 (*current)[sfTransactionMetaData] = next;
2006 current = &((*current)[sfTransactionMetaData]);
2007 }
2008 (*current)[sfTransactionResult.getJsonName()] = 1;
2009 j[sfTransactionMetaData] = obj;
2010 STParsedJSONObject const parsed("Test", j);
2011 BEAST_EXPECT(!parsed.object.has_value());
2012 }
2013 }
2014
2015 void
2017 {
2018 testcase("Array");
2019 // Test with valid array for Array
2020 {
2021 json::Value j;
2024 elem[sfTransactionResult] = 2;
2026 elem2[sfTransactionMetaData] = elem;
2027 arr.append(elem2);
2028 j[sfSignerEntries] = arr;
2029 STParsedJSONObject obj("Test", j);
2030 BEAST_EXPECT(obj.object.has_value());
2031 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2032 BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries));
2033 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2034 auto const& result = obj.object->getFieldArray(sfSignerEntries);
2035 if (BEAST_EXPECT(result.size() == 1))
2036 {
2037 BEAST_EXPECT(result[0].getFName() == sfTransactionMetaData);
2038 BEAST_EXPECT(result[0].getJson(0) == elem);
2039 }
2040 }
2041
2042 // Test with array containing non-object element (should fail)
2043 {
2044 json::Value j;
2046 arr.append("notanobject");
2047 j[sfSignerEntries] = arr;
2048 STParsedJSONObject const obj("Test", j);
2049 BEAST_EXPECT(!obj.object.has_value());
2050 }
2051
2052 // Test with array containing object with invalid field (should fail)
2053 {
2054 json::Value j;
2057 elem["invalidField"] = 1;
2058 arr.append(elem);
2059 j[sfSignerEntries] = arr;
2060 STParsedJSONObject const obj("Test", j);
2061 BEAST_EXPECT(!obj.object.has_value());
2062 }
2063
2064 // Test with array containing object with multiple keys (should fail)
2065 {
2066 json::Value j;
2069 elem[sfTransactionResult] = 2;
2070 elem[sfNetworkID] = 3;
2071 arr.append(elem);
2072 j[sfSignerEntries] = arr;
2073 STParsedJSONObject const obj("Test", j);
2074 BEAST_EXPECT(!obj.object.has_value());
2075 }
2076
2077 // Test with non-array value for Array (should fail)
2078 {
2079 json::Value j;
2080 j[sfSignerEntries] = "notanarray";
2081 STParsedJSONObject const obj("Test", j);
2082 BEAST_EXPECT(!obj.object.has_value());
2083 }
2084
2085 // Test with array containing object with valid field but invalid value
2086 // (should fail)
2087 {
2088 json::Value j;
2091 elem[sfTransactionResult] = "notanint";
2092 arr.append(elem);
2093 j[sfSignerEntries] = arr;
2094 STParsedJSONObject const obj("Test", j);
2095 BEAST_EXPECT(!obj.object.has_value());
2096 }
2097
2098 // Test with empty array for Array (should be valid)
2099 {
2100 json::Value j;
2102 j[sfSignerEntries] = arr;
2103 STParsedJSONObject obj("Test", j);
2104 BEAST_EXPECT(obj.object.has_value());
2105 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2106 BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries));
2107 }
2108
2109 // Test with object provided but not object SField
2110 {
2111 json::Value j;
2114 obj[0u][sfTransactionResult] = 1;
2115 j[sfSignerEntries] = obj;
2116 STParsedJSONObject const parsed("Test", j);
2117 BEAST_EXPECT(!parsed.object.has_value());
2118 }
2119
2120 // Test invalid children
2121 {
2122 try
2123 {
2124 /*
2125
2126 STArray/STObject constructs don't really map perfectly to json
2127 arrays/objects.
2128
2129 STObject is an associative container, mapping fields to value,
2130 but an STObject may also have a Field as its name, stored
2131 outside the associative structure. The name is important, so to
2132 maintain fidelity, it will take TWO json objects to represent
2133 them.
2134
2135 */
2136 std::string const faulty(
2137 "{\"Template\":[{"
2138 "\"ModifiedNode\":{\"Sequence\":1}, "
2139 "\"DeletedNode\":{\"Sequence\":1}"
2140 "}]}");
2141
2143 json::Value faultyJson;
2144 bool const parsedOK(parseJSONString(faulty, faultyJson));
2145 unexpected(!parsedOK, "failed to parse");
2146 STParsedJSONObject const parsed("test", faultyJson);
2147 BEAST_EXPECT(!parsed.object);
2148 }
2149 catch (std::runtime_error const& e)
2150 {
2151 std::string const what(e.what());
2152 unexpected(!what.starts_with("First level children of `Template`"));
2153 }
2154 }
2155 }
2156
2157 void
2159 {
2160 testcase("Array bounds checking");
2161
2162 auto const limitStr = std::to_string(kMaxParsedJsonArraySize) + " elements per field.";
2163
2164 // parseArray rejects oversized STI_ARRAY (SignerEntries)
2165 {
2166 json::Value jv;
2168 for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
2169 {
2170 json::Value entry;
2171 entry["SignerEntry"]["Account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
2172 entry["SignerEntry"]["SignerWeight"] = 1;
2173 entries.append(entry);
2174 }
2175 jv[sfSignerEntries] = entries;
2176
2177 STParsedJSONObject parsed("test", jv);
2178 BEAST_EXPECT(!parsed.object);
2179 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2180 BEAST_EXPECT(
2181 parsed.error[jss::error_message] ==
2182 "Field 'test.SignerEntries' exceeds allowed JSON array size of " + limitStr);
2183 }
2184
2185 // parseObject rejects oversized STI_VECTOR256 (Amendments)
2186 {
2187 json::Value jv;
2189 std::string const hash(64, '0');
2190 for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
2191 amendments.append(hash);
2192 jv[sfAmendments] = amendments;
2193
2194 STParsedJSONObject parsed("test", jv);
2195 BEAST_EXPECT(!parsed.object);
2196 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2197 BEAST_EXPECT(
2198 parsed.error[jss::error_message] ==
2199 "Field 'test.Amendments' exceeds allowed JSON array size of " + limitStr);
2200 }
2201
2202 // parseObject accepts exactly kMaxParsedJsonArraySize STI_VECTOR256 (Amendments)
2203 {
2204 json::Value jv;
2206 std::string const hash(64, '0');
2207 for (std::size_t i = 0; i < kMaxParsedJsonArraySize; ++i)
2208 amendments.append(hash);
2209 jv[sfAmendments] = amendments;
2210
2211 STParsedJSONObject const parsed("test", jv);
2212 BEAST_EXPECT(parsed.object);
2213 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2214 auto const arrSize = parsed.object->getFieldV256(sfAmendments).size();
2215 BEAST_EXPECT(arrSize == kMaxParsedJsonArraySize);
2216 }
2217
2218 // parseObject rejects oversized STI_PATHSET (outer array)
2219 {
2220 json::Value jv;
2222 for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
2223 {
2225 json::Value hop;
2226 hop["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
2227 path.append(hop);
2228 paths.append(path);
2229 }
2230 jv[sfPaths] = paths;
2231
2232 STParsedJSONObject parsed("test", jv);
2233 BEAST_EXPECT(!parsed.object);
2234 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2235 BEAST_EXPECT(
2236 parsed.error[jss::error_message] ==
2237 "Field 'test.Paths' exceeds allowed JSON array size of " + limitStr);
2238 }
2239
2240 // parseObject accepts exactly kMaxParsedJsonArraySize STI_PATHSET (outer array)
2241 {
2242 json::Value jv;
2244 for (std::size_t i = 0; i < kMaxParsedJsonArraySize; ++i)
2245 {
2247 json::Value hop;
2248 hop["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
2249 path.append(hop);
2250 paths.append(path);
2251 }
2252 jv[sfPaths] = paths;
2253
2254 STParsedJSONObject const parsed("test", jv);
2255 BEAST_EXPECT(parsed.object);
2256 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2257 auto const arrSize = parsed.object->getFieldPathSet(sfPaths).size();
2258 BEAST_EXPECT(arrSize == kMaxParsedJsonArraySize);
2259 }
2260
2261 // parseObject rejects oversized STI_PATHSET (inner path hop array)
2262 {
2263 json::Value jv;
2266 json::Value hop;
2267 hop["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
2268 for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
2269 path.append(hop);
2270 paths.append(path);
2271 jv[sfPaths] = paths;
2272
2273 STParsedJSONObject parsed("test", jv);
2274 BEAST_EXPECT(!parsed.object);
2275 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2276 BEAST_EXPECT(
2277 parsed.error[jss::error_message] ==
2278 "Field 'test.Paths[0]' exceeds allowed JSON array size of " + limitStr);
2279 }
2280
2281 // parseObject accepts exactly kMaxParsedJsonArraySize hops in a single STI_PATHSET path
2282 {
2283 json::Value jv;
2286 json::Value hop;
2287 hop["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
2288 for (std::size_t i = 0; i < kMaxParsedJsonArraySize; ++i)
2289 path.append(hop);
2290 paths.append(path);
2291 jv[sfPaths] = paths;
2292
2293 STParsedJSONObject const parsed("test", jv);
2294 BEAST_EXPECT(parsed.object);
2295 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2296 auto const arrSize = parsed.object->getFieldPathSet(sfPaths)[0].size();
2297 BEAST_EXPECT(arrSize == kMaxParsedJsonArraySize);
2298 }
2299
2300 // parseArray accepts exactly kMaxParsedJsonArraySize Memos (boundary)
2301 {
2302 json::Value jv;
2304 for (std::size_t i = 0; i < kMaxParsedJsonArraySize; ++i)
2305 {
2306 json::Value memo;
2307 memo["Memo"] = json::Value(json::ValueType::Object);
2308 memo["Memo"]["MemoData"] = "00";
2309 memos.append(memo);
2310 }
2311 jv[sfMemos] = memos;
2312
2313 STParsedJSONObject const parsed("test", jv);
2314 BEAST_EXPECT(parsed.object);
2315 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2316 auto const arrSize = parsed.object->getFieldArray(sfMemos).size();
2317 BEAST_EXPECT(arrSize == kMaxParsedJsonArraySize);
2318 }
2319
2320 // parseArray rejects one more than kMaxParsedJsonArraySize Memos
2321 {
2322 json::Value jv;
2324 for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
2325 {
2326 json::Value memo;
2327 memo["Memo"] = json::Value(json::ValueType::Object);
2328 memo["Memo"]["MemoData"] = "00";
2329 memos.append(memo);
2330 }
2331 jv[sfMemos] = memos;
2332
2333 STParsedJSONObject parsed("test", jv);
2334 BEAST_EXPECT(!parsed.object);
2335 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2336 BEAST_EXPECT(
2337 parsed.error[jss::error_message] ==
2338 "Field 'test.Memos' exceeds allowed JSON array size of " + limitStr);
2339 }
2340 }
2341
2342 void
2344 {
2345 testcase("General Invalid Cases");
2346
2347 {
2348 json::Value j;
2349 j[sfLedgerEntry] = 1; // not a valid SField for STParsedJSON
2350 }
2351
2352 {
2353 std::string const goodJson(
2354 R"({"CloseResolution":19,"Method":250,)"
2355 R"("TransactionResult":"tecFROZEN"})");
2356
2357 json::Value jv;
2358 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
2359 {
2360 STParsedJSONObject parsed("test", jv);
2361 if (BEAST_EXPECT(parsed.object))
2362 {
2363 std::string const& serialized(
2364 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2365 to_string(parsed.object->getJson(JsonOptions::Values::None)));
2366 BEAST_EXPECT(serialized == goodJson);
2367 }
2368 }
2369 }
2370
2371 {
2372 std::string const goodJson(
2373 R"({"CloseResolution":19,"Method":"250",)"
2374 R"("TransactionResult":"tecFROZEN"})");
2375 std::string const expectedJson(
2376 R"({"CloseResolution":19,"Method":250,)"
2377 R"("TransactionResult":"tecFROZEN"})");
2378
2379 json::Value jv;
2380 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
2381 {
2382 // Integer values are always parsed as int,
2383 // unless they're too big. We want a small uint.
2384 jv["CloseResolution"] = json::UInt(19);
2385 STParsedJSONObject parsed("test", jv);
2386 if (BEAST_EXPECT(parsed.object))
2387 {
2388 std::string const& serialized(
2389 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2390 to_string(parsed.object->getJson(JsonOptions::Values::None)));
2391 BEAST_EXPECT(serialized == expectedJson);
2392 }
2393 }
2394 }
2395
2396 {
2397 std::string const goodJson(
2398 R"({"CloseResolution":"19","Method":"250",)"
2399 R"("TransactionResult":"tecFROZEN"})");
2400 std::string const expectedJson(
2401 R"({"CloseResolution":19,"Method":250,)"
2402 R"("TransactionResult":"tecFROZEN"})");
2403
2404 json::Value jv;
2405 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
2406 {
2407 // Integer values are always parsed as int,
2408 // unless they're too big. We want a small uint.
2409 jv["CloseResolution"] = json::UInt(19);
2410 STParsedJSONObject parsed("test", jv);
2411 if (BEAST_EXPECT(parsed.object))
2412 {
2413 std::string const& serialized(
2414 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2415 to_string(parsed.object->getJson(JsonOptions::Values::None)));
2416 BEAST_EXPECT(serialized == expectedJson);
2417 }
2418 }
2419 }
2420
2421 {
2422 std::string const json(
2423 R"({"CloseResolution":19,"Method":250,)"
2424 R"("TransactionResult":"terQUEUED"})");
2425
2426 json::Value jv;
2427 if (BEAST_EXPECT(parseJSONString(json, jv)))
2428 {
2429 STParsedJSONObject parsed("test", jv);
2430 BEAST_EXPECT(!parsed.object);
2431 BEAST_EXPECT(parsed.error);
2432 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2433 BEAST_EXPECT(
2434 parsed.error[jss::error_message] ==
2435 "Field 'test.TransactionResult' is out of range.");
2436 }
2437 }
2438
2439 {
2440 std::string const json(
2441 R"({"CloseResolution":19,"Method":"pony",)"
2442 R"("TransactionResult":"tesSUCCESS"})");
2443
2444 json::Value jv;
2445 if (BEAST_EXPECT(parseJSONString(json, jv)))
2446 {
2447 STParsedJSONObject parsed("test", jv);
2448 BEAST_EXPECT(!parsed.object);
2449 BEAST_EXPECT(parsed.error);
2450 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2451 BEAST_EXPECT(
2452 parsed.error[jss::error_message] == "Field 'test.Method' has bad type.");
2453 }
2454 }
2455
2456 {
2457 std::string const json(
2458 R"({"CloseResolution":19,"Method":3294967296,)"
2459 R"("TransactionResult":"tesSUCCESS"})");
2460
2461 json::Value jv;
2462 if (BEAST_EXPECT(parseJSONString(json, jv)))
2463 {
2464 STParsedJSONObject parsed("test", jv);
2465 BEAST_EXPECT(!parsed.object);
2466 BEAST_EXPECT(parsed.error);
2467 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2468 BEAST_EXPECT(
2469 parsed.error[jss::error_message] == "Field 'test.Method' is out of range.");
2470 }
2471 }
2472
2473 {
2474 std::string const json(
2475 R"({"CloseResolution":-10,"Method":42,)"
2476 R"("TransactionResult":"tesSUCCESS"})");
2477
2478 json::Value jv;
2479 if (BEAST_EXPECT(parseJSONString(json, jv)))
2480 {
2481 STParsedJSONObject parsed("test", jv);
2482 BEAST_EXPECT(!parsed.object);
2483 BEAST_EXPECT(parsed.error);
2484 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2485 BEAST_EXPECT(
2486 parsed.error[jss::error_message] ==
2487 "Field 'test.CloseResolution' is out of range.");
2488 }
2489 }
2490
2491 {
2492 std::string const json(
2493 R"({"CloseResolution":19,"Method":3.141592653,)"
2494 R"("TransactionResult":"tesSUCCESS"})");
2495
2496 json::Value jv;
2497 if (BEAST_EXPECT(parseJSONString(json, jv)))
2498 {
2499 STParsedJSONObject parsed("test", jv);
2500 BEAST_EXPECT(!parsed.object);
2501 BEAST_EXPECT(parsed.error);
2502 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2503 BEAST_EXPECT(
2504 parsed.error[jss::error_message] == "Field 'test.Method' has bad type.");
2505 }
2506 }
2507
2508 {
2509 std::string const goodJson(
2510 R"({"CloseResolution":19,"Method":250,)"
2511 R"("TransferFee":"65535"})");
2512 std::string const expectedJson(
2513 R"({"CloseResolution":19,"Method":250,)"
2514 R"("TransferFee":65535})");
2515
2516 json::Value jv;
2517 if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
2518 {
2519 STParsedJSONObject parsed("test", jv);
2520 if (BEAST_EXPECT(parsed.object))
2521 {
2522 std::string const& serialized(
2523 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
2524 to_string(parsed.object->getJson(JsonOptions::Values::None)));
2525 BEAST_EXPECT(serialized == expectedJson);
2526 }
2527 }
2528 }
2529
2530 {
2531 std::string const json(
2532 R"({"CloseResolution":19,"Method":250,)"
2533 R"("TransferFee":"65536"})");
2534
2535 json::Value jv;
2536 if (BEAST_EXPECT(parseJSONString(json, jv)))
2537 {
2538 STParsedJSONObject parsed("test", jv);
2539 BEAST_EXPECT(!parsed.object);
2540 BEAST_EXPECT(parsed.error);
2541 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2542 BEAST_EXPECT(
2543 parsed.error[jss::error_message] ==
2544 "Field 'test.TransferFee' has invalid data.");
2545 }
2546 }
2547
2548 {
2549 std::string const json(
2550 R"({"CloseResolution":19,"Method":250,)"
2551 R"("TransferFee":"Payment"})");
2552
2553 json::Value jv;
2554 if (BEAST_EXPECT(parseJSONString(json, jv)))
2555 {
2556 STParsedJSONObject parsed("test", jv);
2557 BEAST_EXPECT(!parsed.object);
2558 BEAST_EXPECT(parsed.error);
2559 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2560 BEAST_EXPECT(
2561 parsed.error[jss::error_message] ==
2562 "Field 'test.TransferFee' has invalid data.");
2563 }
2564 }
2565
2566 {
2567 std::string const json(
2568 R"({"CloseResolution":19,"Method":250,)"
2569 R"("TransferFee":true})");
2570
2571 json::Value jv;
2572 if (BEAST_EXPECT(parseJSONString(json, jv)))
2573 {
2574 STParsedJSONObject parsed("test", jv);
2575 BEAST_EXPECT(!parsed.object);
2576 BEAST_EXPECT(parsed.error);
2577 BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
2578 BEAST_EXPECT(
2579 parsed.error[jss::error_message] == "Field 'test.TransferFee' has bad type.");
2580 }
2581 }
2582 }
2583
2584 void
2585 run() override
2586 {
2587 // Instantiate a jtx::Env so debugLog writes are exercised.
2588 test::jtx::Env const env(*this);
2589 testUInt8();
2590 testUInt16();
2591 testUInt32();
2592 testUInt64();
2593 testUInt128();
2594 testUInt160();
2595 testUInt192();
2596 testUInt256();
2597 testInt32();
2598 testBlob();
2599 testVector256();
2600 testAccount();
2601 testCurrency();
2602 testAmount();
2603 testPathSet();
2604 testIssue();
2606 testNumber();
2607 testObject();
2608 testArray();
2610 testEdgeCases();
2611 }
2612};
2613
2615
2616} // namespace xrpl
T all_of(T... args)
A testsuite class.
Definition suite.h:52
bool unexpected(Condition shouldBeFalse, String const &reason)
DEPRECATED.
Definition suite.h:516
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Unserialize a JSON document into a Value.
Definition json_reader.h:20
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:117
bool isObject() const
Value & append(Value const &value)
Append value to array at the end.
static BaseUInt fromRaw(Container const &c)
Definition base_uint.h:302
A currency issued by an account.
Definition Issue.h:18
Number is a floating point type that can represent a wide range of values.
Definition Number.h:351
Holds the serialized result of parsing an input JSON object.
std::optional< STObject > object
The STObject if the parse was successful.
json::Value error
On failure, an appropriate set of error values.
static bool parseJSONString(std::string const &json, json::Value &to)
void run() override
Runs the suite.
A transaction testing environment.
Definition Env.h:161
JSON (JavaScript Object Notation).
Definition json_errors.h:5
unsigned int UInt
@ Array
array value (ordered list)
Definition json_value.h:28
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
@ Null
'null' value
Definition json_value.h:22
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
constexpr std::size_t kMaxParsedJsonDepth
Maximum JSON object nesting depth permitted during parsing.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
constexpr std::size_t kMaxParsedJsonArraySize
Maximum number of elements permitted in any JSON array field during parsing.
json::Value getJson(LedgerFill const &fill)
Return a new json::Value representing the ledger with given options.
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
T starts_with(T... args)
T to_string(T... args)
T what(T... args)