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