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