mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-09 21:42:25 +00:00
2415 lines
88 KiB
C++
2415 lines
88 KiB
C++
#include <test/jtx.h>
|
|
|
|
#include <xrpl/beast/unit_test.h>
|
|
#include <xrpl/json/json_reader.h>
|
|
#include <xrpl/protocol/STNumber.h>
|
|
#include <xrpl/protocol/STParsedJSON.h>
|
|
#include <xrpl/protocol/STXChainBridge.h>
|
|
#include <xrpl/protocol/st.h>
|
|
|
|
namespace xrpl {
|
|
|
|
class STParsedJSON_test : public beast::unit_test::suite
|
|
{
|
|
static bool
|
|
parseJSONString(std::string const& json, Json::Value& to)
|
|
{
|
|
Json::Reader reader;
|
|
return reader.parse(json, to) && to.isObject();
|
|
}
|
|
|
|
void
|
|
testUInt8()
|
|
{
|
|
testcase("UInt8");
|
|
{
|
|
Json::Value j;
|
|
j[sfCloseResolution] = 255;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255);
|
|
}
|
|
|
|
// test with uint value
|
|
{
|
|
Json::Value j;
|
|
j[sfCloseResolution] = 255u;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255);
|
|
}
|
|
|
|
// Test with string value
|
|
{
|
|
Json::Value j;
|
|
j[sfCloseResolution] = "255";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfCloseResolution));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 255);
|
|
}
|
|
|
|
// Test min value for uint8
|
|
{
|
|
Json::Value j;
|
|
j[sfCloseResolution] = 0;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU8(sfCloseResolution) == 0);
|
|
}
|
|
|
|
// Test out of range value for UInt8 (negative)
|
|
{
|
|
Json::Value j;
|
|
j[sfCloseResolution] = -1;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test out of range value for UInt8 (too large)
|
|
{
|
|
Json::Value j;
|
|
j[sfCloseResolution] = 256;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test bad_type (not a string/int/uint)
|
|
{
|
|
Json::Value j;
|
|
j[sfCloseResolution] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test bad_type (not a string/int/uint)
|
|
{
|
|
Json::Value j;
|
|
j[sfCloseResolution] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testUInt16()
|
|
{
|
|
testcase("UInt16");
|
|
// Test with int value
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = 65535;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535);
|
|
}
|
|
|
|
// Test with uint value
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = 65535u;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535);
|
|
}
|
|
|
|
// Test with string value
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = "65535";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerEntryType));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 65535);
|
|
}
|
|
|
|
// Test min value for uint16
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = 0;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU16(sfLedgerEntryType) == 0);
|
|
}
|
|
|
|
// Test out of range value for UInt16 (negative)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = -1;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test out of range value for UInt16 (too large)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = 65536;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test string value out of range
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = "65536";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test bad_type (not a string/int/uint)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test bad_type (not a string/int/uint)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntryType] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid input for other field
|
|
{
|
|
Json::Value j;
|
|
j[sfTransferFee] = "Payment";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testUInt32()
|
|
{
|
|
testcase("UInt32");
|
|
{
|
|
Json::Value j;
|
|
j[sfNetworkID] = 4294967295u;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u);
|
|
}
|
|
|
|
// Test with string value
|
|
{
|
|
Json::Value j;
|
|
j[sfNetworkID] = "4294967295";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfNetworkID));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 4294967295u);
|
|
}
|
|
|
|
// Test min value for uint32
|
|
{
|
|
Json::Value j;
|
|
j[sfNetworkID] = 0;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU32(sfNetworkID) == 0);
|
|
}
|
|
|
|
// Test out of range value for uint32 (negative)
|
|
{
|
|
Json::Value j;
|
|
j[sfNetworkID] = -1;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test string value out of range
|
|
{
|
|
Json::Value j;
|
|
j[sfNetworkID] = "4294967296";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test bad_type (arrayValue)
|
|
{
|
|
Json::Value j;
|
|
j[sfNetworkID] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test bad_type (objectValue)
|
|
{
|
|
Json::Value j;
|
|
j[sfNetworkID] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testUInt64()
|
|
{
|
|
testcase("UInt64");
|
|
{
|
|
Json::Value j;
|
|
j[sfIndexNext] = "ffffffffffffffff";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfIndexNext));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU64(sfIndexNext) == 18446744073709551615ull);
|
|
}
|
|
|
|
// Test min value for uint64
|
|
{
|
|
Json::Value j;
|
|
j[sfIndexNext] = 0;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldU64(sfIndexNext) == 0ull);
|
|
}
|
|
|
|
// Test out of range value for uint64 (negative)
|
|
{
|
|
Json::Value j;
|
|
j[sfIndexNext] = -1;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// NOTE: the JSON parser doesn't support > UInt32, so those values must
|
|
// be in hex
|
|
// Test string value out of range
|
|
// string is interpreted as hex
|
|
{
|
|
Json::Value j;
|
|
j[sfIndexNext] = "10000000000000000"; // uint64 max + 1 (in hex)
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test hex string value with 0x prefix (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfIndexNext] = "0xabcdefabcdef";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test hex string value with invalid characters
|
|
{
|
|
Json::Value j;
|
|
j[sfIndexNext] = "abcdefga";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// test arrayValue
|
|
{
|
|
Json::Value j;
|
|
j[sfIndexNext] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// test objectValue
|
|
{
|
|
Json::Value j;
|
|
j[sfIndexNext] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testUInt128()
|
|
{
|
|
testcase("UInt128");
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16);
|
|
std::array<uint8_t, 16> const expected = {
|
|
0x01,
|
|
0x23,
|
|
0x45,
|
|
0x67,
|
|
0x89,
|
|
0xAB,
|
|
0xCD,
|
|
0xEF,
|
|
0x01,
|
|
0x23,
|
|
0x45,
|
|
0x67,
|
|
0x89,
|
|
0xAB,
|
|
0xCD,
|
|
0xEF};
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash) == uint128{expected});
|
|
}
|
|
|
|
// Valid lowercase hex string for UInt128
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = "0123456789abcdef0123456789abcdef";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16);
|
|
}
|
|
|
|
// Empty string for UInt128 (should be valid, all zero)
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = "";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& h128 = obj.object->getFieldH128(sfEmailHash);
|
|
BEAST_EXPECT(h128.size() == 16);
|
|
bool const allZero =
|
|
std::all_of(h128.begin(), h128.end(), [](auto b) { return b == 0; });
|
|
BEAST_EXPECT(allZero);
|
|
}
|
|
|
|
// Odd-length hex string for UInt128 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDE";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Non-hex string for UInt128 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = "nothexstring";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Hex string too short for UInt128 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = "01234567";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Hex string too long for UInt128 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF00";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Array value for UInt128 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Object value for UInt128 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfEmailHash] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testUInt160()
|
|
{
|
|
testcase("UInt160");
|
|
{
|
|
Json::Value j;
|
|
j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF01234567";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20);
|
|
std::array<uint8_t, 20> const expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD,
|
|
0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
|
|
0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67};
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency) == uint160{expected});
|
|
}
|
|
// Valid lowercase hex string for UInt160
|
|
{
|
|
Json::Value j;
|
|
j[sfTakerPaysCurrency] = "0123456789abcdef0123456789abcdef01234567";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20);
|
|
}
|
|
|
|
// Empty string for UInt160 (should be valid, all zero)
|
|
{
|
|
Json::Value j;
|
|
j[sfTakerPaysCurrency] = "";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& h160 = obj.object->getFieldH160(sfTakerPaysCurrency);
|
|
BEAST_EXPECT(h160.size() == 20);
|
|
bool const allZero =
|
|
std::all_of(h160.begin(), h160.end(), [](auto b) { return b == 0; });
|
|
BEAST_EXPECT(allZero);
|
|
}
|
|
|
|
// Non-hex string for UInt160 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfTakerPaysCurrency] = "nothexstring";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Hex string too short for UInt160 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfTakerPaysCurrency] = "01234567";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Hex string too long for UInt160 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF0123456789";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Array value for UInt160 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfTakerPaysCurrency] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Object value for UInt160 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfTakerPaysCurrency] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testUInt192()
|
|
{
|
|
testcase("UInt192");
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24);
|
|
std::array<uint8_t, 24> const expected = {
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID) == uint192{expected});
|
|
}
|
|
|
|
// Valid lowercase hex string for UInt192
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = "ffffffffffffffffffffffffffffffffffffffffffffffff";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24);
|
|
}
|
|
|
|
// Empty string for UInt192 (should be valid, all zero)
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = "";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& h192 = obj.object->getFieldH192(sfMPTokenIssuanceID);
|
|
BEAST_EXPECT(h192.size() == 24);
|
|
bool const allZero =
|
|
std::all_of(h192.begin(), h192.end(), [](auto b) { return b == 0; });
|
|
BEAST_EXPECT(allZero);
|
|
}
|
|
|
|
// Odd-length hex string for UInt192 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDE";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Non-hex string for UInt192 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = "nothexstring";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Hex string too short for UInt192 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = "01234567";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Hex string too long for UInt192 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF00";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Array value for UInt192 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Object value for UInt192 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfMPTokenIssuanceID] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testUInt256()
|
|
{
|
|
testcase("UInt256");
|
|
// Test with valid hex string for UInt256
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] =
|
|
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD"
|
|
"EF";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32);
|
|
std::array<uint8_t, 32> const expected = {
|
|
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45,
|
|
0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
|
|
0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash) == uint256{expected});
|
|
}
|
|
// Valid lowercase hex string for UInt256
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] =
|
|
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd"
|
|
"ef";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32);
|
|
}
|
|
|
|
// Empty string for UInt256 (should be valid, all zero)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] = "";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& h256 = obj.object->getFieldH256(sfLedgerHash);
|
|
BEAST_EXPECT(h256.size() == 32);
|
|
bool const allZero =
|
|
std::all_of(h256.begin(), h256.end(), [](auto b) { return b == 0; });
|
|
BEAST_EXPECT(allZero);
|
|
}
|
|
|
|
// Odd-length hex string for UInt256 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] =
|
|
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD"
|
|
"E";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Non-hex string for UInt256 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] = "nothexstring";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Hex string too short for UInt256 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] = "01234567";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Hex string too long for UInt256 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] =
|
|
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD"
|
|
"EF00";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Array value for UInt256 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Object value for UInt256 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerHash] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testInt32()
|
|
{
|
|
testcase("Int32");
|
|
{
|
|
Json::Value j;
|
|
int const minInt32 = -2147483648;
|
|
j[sfLoanScale] = minInt32;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
|
|
{
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == minInt32);
|
|
}
|
|
}
|
|
|
|
// max value
|
|
{
|
|
Json::Value j;
|
|
int const maxInt32 = 2147483647;
|
|
j[sfLoanScale] = maxInt32;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
|
|
{
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == maxInt32);
|
|
}
|
|
}
|
|
|
|
// max uint value
|
|
{
|
|
Json::Value j;
|
|
unsigned int const maxUInt32 = 2147483647u;
|
|
j[sfLoanScale] = maxUInt32;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
|
|
{
|
|
BEAST_EXPECT(
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
obj.object->getFieldI32(sfLoanScale) == static_cast<int32_t>(maxUInt32));
|
|
}
|
|
}
|
|
|
|
// Test with string value
|
|
{
|
|
Json::Value j;
|
|
j[sfLoanScale] = "2147483647";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
|
|
{
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == 2147483647u);
|
|
}
|
|
}
|
|
|
|
// Test with string negative value
|
|
{
|
|
Json::Value j;
|
|
int const value = -2147483648;
|
|
j[sfLoanScale] = std::to_string(value);
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
if (BEAST_EXPECT(obj.object->isFieldPresent(sfLoanScale)))
|
|
{
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldI32(sfLoanScale) == value);
|
|
}
|
|
}
|
|
|
|
// Test out of range value for int32 (negative)
|
|
{
|
|
Json::Value j;
|
|
j[sfLoanScale] = "-2147483649";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test out of range value for int32 (positive)
|
|
{
|
|
Json::Value j;
|
|
j[sfLoanScale] = 2147483648u;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test string value out of range
|
|
{
|
|
Json::Value j;
|
|
j[sfLoanScale] = "2147483648";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test bad_type (arrayValue)
|
|
{
|
|
Json::Value j;
|
|
j[sfLoanScale] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test bad_type (objectValue)
|
|
{
|
|
Json::Value j;
|
|
j[sfLoanScale] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testBlob()
|
|
{
|
|
testcase("Blob");
|
|
// Test with valid hex string for blob
|
|
{
|
|
Json::Value j;
|
|
j[sfPublicKey] = "DEADBEEF";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey));
|
|
auto const& blob =
|
|
obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(blob.size() == 4);
|
|
BEAST_EXPECT(blob[0] == 0xDE);
|
|
BEAST_EXPECT(blob[1] == 0xAD);
|
|
BEAST_EXPECT(blob[2] == 0xBE);
|
|
BEAST_EXPECT(blob[3] == 0xEF);
|
|
}
|
|
|
|
// Test empty string for blob (should be valid, size 0)
|
|
{
|
|
Json::Value j;
|
|
j[sfPublicKey] = "";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey));
|
|
auto const& blob =
|
|
obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(blob.empty());
|
|
}
|
|
|
|
// Test lowercase hex string for blob
|
|
{
|
|
Json::Value j;
|
|
j[sfPublicKey] = "deadbeef";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfPublicKey));
|
|
auto const& blob =
|
|
obj.object->getFieldVL(sfPublicKey); // NOLINT(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(blob.size() == 4);
|
|
BEAST_EXPECT(blob[0] == 0xDE);
|
|
BEAST_EXPECT(blob[1] == 0xAD);
|
|
BEAST_EXPECT(blob[2] == 0xBE);
|
|
BEAST_EXPECT(blob[3] == 0xEF);
|
|
}
|
|
|
|
// Test non-hex string for blob (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfPublicKey] = "XYZ123";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test array value for blob (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfPublicKey] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test object value for blob (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfPublicKey] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testVector256()
|
|
{
|
|
testcase("Vector256");
|
|
// Test with valid array of hex strings for Vector256
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
arr.append(
|
|
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD"
|
|
"EF");
|
|
arr.append(
|
|
"FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA98765432"
|
|
"10");
|
|
j[sfHashes] = arr;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfHashes));
|
|
auto const& vec =
|
|
obj.object->getFieldV256(sfHashes); // NOLINT(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(vec.size() == 2);
|
|
BEAST_EXPECT(to_string(vec[0]) == arr[0u].asString());
|
|
BEAST_EXPECT(to_string(vec[1]) == arr[1u].asString());
|
|
}
|
|
// Test empty array for Vector256 (should be valid, size 0)
|
|
{
|
|
Json::Value j;
|
|
Json::Value const arr(Json::arrayValue);
|
|
j[sfHashes] = arr;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfHashes));
|
|
auto const& vec =
|
|
obj.object->getFieldV256(sfHashes); // NOLINT(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(vec.empty());
|
|
}
|
|
|
|
// Test array with invalid hex string (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
arr.append("nothexstring");
|
|
j[sfHashes] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test array with string of wrong length (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
arr.append("0123456789ABCDEF"); // too short for uint256
|
|
j[sfHashes] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test array with non-string element (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
arr.append(12345);
|
|
j[sfHashes] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test non-array value for Vector256 (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfHashes] = "notanarray";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test array with object element (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
Json::Value objElem(Json::objectValue);
|
|
objElem["foo"] = "bar";
|
|
arr.append(objElem);
|
|
j[sfHashes] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testAccount()
|
|
{
|
|
testcase("Account");
|
|
// Test with valid base58 string for AccountID
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfAccount));
|
|
auto const& acct =
|
|
obj.object->getAccountID(sfAccount); // NOLINT(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(acct.size() == 20);
|
|
BEAST_EXPECT(toBase58(acct) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
|
|
}
|
|
|
|
// Valid hex string for AccountID
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = "000102030405060708090A0B0C0D0E0F10111213";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfAccount));
|
|
auto const& acct =
|
|
obj.object->getAccountID(sfAccount); // NOLINT(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(acct.size() == 20);
|
|
}
|
|
|
|
// Invalid base58 string for AccountID
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = "notAValidBase58Account";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid hex string for AccountID (too short)
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = "001122334455";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid hex string for AccountID (too long)
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = "000102030405060708090A0B0C0D0E0F101112131415";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid hex string for AccountID (bad chars)
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = "000102030405060708090A0B0C0D0E0F1011121G";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Empty string for AccountID (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = "";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Array value for AccountID (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Object value for AccountID (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfAccount] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testCurrency()
|
|
{
|
|
testcase("Currency");
|
|
// Test with valid ISO code for currency
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = "USD";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
|
|
BEAST_EXPECT(curr.currency().size() == 20);
|
|
}
|
|
|
|
// Valid ISO code
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = "EUR";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
|
|
BEAST_EXPECT(curr.currency().size() == 20);
|
|
}
|
|
|
|
// Valid hex string for currency
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = "0123456789ABCDEF01230123456789ABCDEF0123";
|
|
STParsedJSONObject obj("Test", j);
|
|
if (BEAST_EXPECT(obj.object); obj.object.has_value())
|
|
{
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
|
|
auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
|
|
BEAST_EXPECT(curr.currency().size() == 20);
|
|
}
|
|
}
|
|
|
|
// Invalid ISO code (too long)
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = "USDD";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// lowercase ISO code
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = "usd";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
|
|
BEAST_EXPECT(curr.currency().size() == 20);
|
|
}
|
|
|
|
// Invalid hex string (too short)
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = "0123456789AB";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid hex string (too long)
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = "0123456789ABCDEF0123456789";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Empty string for currency (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = "";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfBaseAsset));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& curr = obj.object->getFieldCurrency(sfBaseAsset);
|
|
BEAST_EXPECT(curr.currency().size() == 20);
|
|
}
|
|
|
|
// Array value for currency (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Object value for currency (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfBaseAsset] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testAmount()
|
|
{
|
|
testcase("Amount");
|
|
// Test with string value for Amount
|
|
{
|
|
Json::Value j;
|
|
j[sfAmount] = "100000000000000000";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfAmount));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(100000000000000000ull));
|
|
}
|
|
|
|
// Test with int value for Amount
|
|
{
|
|
Json::Value j;
|
|
j[sfAmount] = 4294967295u;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfAmount));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldAmount(sfAmount) == STAmount(4294967295u));
|
|
}
|
|
|
|
// Test with decimal string for Amount (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfAmount] = "123.45";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with empty string for Amount (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfAmount] = "";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with non-numeric string for Amount (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfAmount] = "notanumber";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with object value for Amount (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfAmount] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testPathSet()
|
|
{
|
|
testcase("PathSet");
|
|
// Valid test: single path with single element
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
elem["currency"] = "USD";
|
|
elem["issuer"] = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe";
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject obj("Test", j);
|
|
if (BEAST_EXPECT(obj.object.has_value()))
|
|
{
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfPaths));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& ps = obj.object->getFieldPathSet(sfPaths);
|
|
BEAST_EXPECT(!ps.empty());
|
|
BEAST_EXPECT(ps.size() == 1);
|
|
BEAST_EXPECT(ps[0].size() == 1);
|
|
BEAST_EXPECT(
|
|
ps[0][0].getAccountID() ==
|
|
parseBase58<AccountID>("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"));
|
|
BEAST_EXPECT(to_string(ps[0][0].getCurrency()) == "USD");
|
|
BEAST_EXPECT(
|
|
ps[0][0].getIssuerID() ==
|
|
parseBase58<AccountID>("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"));
|
|
}
|
|
}
|
|
|
|
// Valid test: non-standard currency code
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
elem["currency"] = "0123456789ABCDEF01230123456789ABCDEF0123";
|
|
elem["issuer"] = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe";
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
BEAST_EXPECT(
|
|
obj.object->isFieldPresent(sfPaths)); // NOLINT(bugprone-unchecked-optional-access)
|
|
auto const& ps =
|
|
obj.object->getFieldPathSet(sfPaths); // NOLINT(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(!ps.empty());
|
|
}
|
|
|
|
// Test with non-array value for PathSet (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfPaths] = "notanarray";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with array containing non-array element (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append("notanarray");
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with array containing array with non-object element (should
|
|
// fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
path.append("notanobject");
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with array containing array with object missing required keys
|
|
// (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["foo"] = "bar"; // not a valid path element key
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with array containing array with object with invalid account
|
|
// value (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["account"] = "notAValidBase58Account";
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with account not string (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["account"] = 12345;
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with currency not string (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["currency"] = 12345;
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with non-standard currency not hex (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["currency"] = "notAValidCurrency";
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with issuer not string (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["issuer"] = 12345;
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with issuer not base58 (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value path(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["issuer"] = "notAValidBase58Account";
|
|
path.append(elem);
|
|
Json::Value pathset(Json::arrayValue);
|
|
pathset.append(path);
|
|
j[sfPaths] = pathset;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testIssue()
|
|
{
|
|
testcase("Issue");
|
|
// Valid Issue: currency and issuer as base58
|
|
{
|
|
Json::Value j;
|
|
Json::Value issueJson(Json::objectValue);
|
|
issueJson["currency"] = "USD";
|
|
issueJson["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfAsset] = issueJson;
|
|
STParsedJSONObject obj("Test", j);
|
|
if (BEAST_EXPECT(obj.object.has_value()))
|
|
{
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfAsset));
|
|
auto const& issueField =
|
|
(*obj.object)[sfAsset]; // NOLINT(bugprone-unchecked-optional-access)
|
|
auto const issue = issueField.value().get<Issue>();
|
|
BEAST_EXPECT(issue.currency.size() == 20);
|
|
BEAST_EXPECT(to_string(issue.currency) == "USD");
|
|
BEAST_EXPECT(issue.account.size() == 20);
|
|
BEAST_EXPECT(
|
|
issue.account == parseBase58<AccountID>("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"));
|
|
}
|
|
}
|
|
|
|
// Valid Issue: currency as hex
|
|
{
|
|
Json::Value j;
|
|
Json::Value issueJson(Json::objectValue);
|
|
issueJson["currency"] = "0123456789ABCDEF01230123456789ABCDEF0123";
|
|
issueJson["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfAsset] = issueJson;
|
|
STParsedJSONObject obj("Test", j);
|
|
if (BEAST_EXPECT(obj.object); obj.object.has_value())
|
|
{
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfAsset));
|
|
auto const& issueField = (*obj.object)[sfAsset];
|
|
auto const issue = issueField.value().get<Issue>();
|
|
BEAST_EXPECT(issue.currency.size() == 20);
|
|
BEAST_EXPECT(issue.account.size() == 20);
|
|
}
|
|
}
|
|
|
|
// Valid Issue: MPTID
|
|
{
|
|
Json::Value j;
|
|
Json::Value issueJson(Json::objectValue);
|
|
issueJson["mpt_issuance_id"] = "0000000000000000000000004D5054494431323334234234";
|
|
j[sfAsset] = issueJson;
|
|
STParsedJSONObject obj("Test", j);
|
|
if (BEAST_EXPECT(obj.object); obj.object.has_value())
|
|
{
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfAsset));
|
|
auto const& issueField = (*obj.object)[sfAsset];
|
|
auto const issue = issueField.value().get<MPTIssue>();
|
|
BEAST_EXPECT(issue.getMptID().size() == 24);
|
|
}
|
|
}
|
|
|
|
// Invalid Issue: missing currency
|
|
{
|
|
Json::Value j;
|
|
Json::Value issue(Json::objectValue);
|
|
issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfAsset] = issue;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid Issue: missing issuer
|
|
{
|
|
Json::Value j;
|
|
Json::Value issue(Json::objectValue);
|
|
issue["currency"] = "USD";
|
|
j[sfAsset] = issue;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid Issue: currency too long
|
|
{
|
|
Json::Value j;
|
|
Json::Value issue(Json::objectValue);
|
|
issue["currency"] = "USDD";
|
|
issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfAsset] = issue;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid Issue: issuer not base58 or hex
|
|
{
|
|
Json::Value j;
|
|
Json::Value issue(Json::objectValue);
|
|
issue["currency"] = "USD";
|
|
issue["issuer"] = "notAValidIssuer";
|
|
j[sfAsset] = issue;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid Issue: currency not string
|
|
{
|
|
Json::Value j;
|
|
Json::Value issue(Json::objectValue);
|
|
issue["currency"] = Json::Value(Json::arrayValue);
|
|
issue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfAsset] = issue;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid Issue: issuer not string
|
|
{
|
|
Json::Value j;
|
|
Json::Value issue(Json::objectValue);
|
|
issue["currency"] = "USD";
|
|
issue["issuer"] = Json::Value(Json::objectValue);
|
|
j[sfAsset] = issue;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid Issue: not an object
|
|
{
|
|
Json::Value j;
|
|
j[sfAsset] = "notanobject";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testXChainBridge()
|
|
{
|
|
testcase("XChainBridge");
|
|
// Valid XChainBridge
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value issuingChainIssue(Json::objectValue);
|
|
issuingChainIssue["currency"] = "USD";
|
|
issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
Json::Value lockingChainIssue(Json::objectValue);
|
|
lockingChainIssue["currency"] = "EUR";
|
|
lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["LockingChainIssue"] = lockingChainIssue;
|
|
bridge["IssuingChainIssue"] = issuingChainIssue;
|
|
bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject obj("Test", j);
|
|
if (BEAST_EXPECT(obj.object); obj.object.has_value())
|
|
{
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfXChainBridge));
|
|
auto const& bridgeField = (*obj.object)[sfXChainBridge];
|
|
BEAST_EXPECT(bridgeField->lockingChainIssue().currency.size() == 20);
|
|
BEAST_EXPECT(bridgeField->issuingChainIssue().currency.size() == 20);
|
|
}
|
|
}
|
|
|
|
// Valid XChainBridge: issues as hex currency
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value issuingChainIssue(Json::objectValue);
|
|
issuingChainIssue["currency"] = "0123456789ABCDEF01230123456789ABCDEF0123";
|
|
issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
Json::Value lockingChainIssue(Json::objectValue);
|
|
lockingChainIssue["currency"] = "0123456789ABCDEF01230123456789ABCDEF0123";
|
|
lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["LockingChainIssue"] = lockingChainIssue;
|
|
bridge["IssuingChainIssue"] = issuingChainIssue;
|
|
bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject obj("Test", j);
|
|
if (BEAST_EXPECT(obj.object); obj.object.has_value())
|
|
{
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfXChainBridge));
|
|
auto const& bridgeField = (*obj.object)[sfXChainBridge];
|
|
BEAST_EXPECT(bridgeField->lockingChainIssue().currency.size() == 20);
|
|
BEAST_EXPECT(bridgeField->issuingChainIssue().currency.size() == 20);
|
|
}
|
|
}
|
|
|
|
// Invalid XChainBridge: missing LockingChainIssue
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value issuingChainIssue(Json::objectValue);
|
|
issuingChainIssue["currency"] = "USD";
|
|
issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["IssuingChainIssue"] = issuingChainIssue;
|
|
bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid XChainBridge: missing IssuingChainIssue
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value lockingChainIssue(Json::objectValue);
|
|
lockingChainIssue["currency"] = "EUR";
|
|
lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["LockingChainIssue"] = lockingChainIssue;
|
|
bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid XChainBridge: missing LockingChainDoor
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value issuingChainIssue(Json::objectValue);
|
|
issuingChainIssue["currency"] = "USD";
|
|
issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["IssuingChainIssue"] = issuingChainIssue;
|
|
Json::Value lockingChainIssue(Json::objectValue);
|
|
lockingChainIssue["currency"] = "EUR";
|
|
lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["LockingChainIssue"] = lockingChainIssue;
|
|
bridge["IssuingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid XChainBridge: missing IssuingChainDoor
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value issuingChainIssue(Json::objectValue);
|
|
issuingChainIssue["currency"] = "USD";
|
|
issuingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["IssuingChainIssue"] = issuingChainIssue;
|
|
Json::Value lockingChainIssue(Json::objectValue);
|
|
lockingChainIssue["currency"] = "EUR";
|
|
lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["LockingChainIssue"] = lockingChainIssue;
|
|
bridge["LockingChainDoor"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid XChainBridge: IssuingChainIssue not an object
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
bridge["LockingChainIssue"] = "notanobject";
|
|
bridge["IssuingChainIssue"] = "notanobject";
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid XChainBridge: IssuingChainIssue missing currency
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value asset(Json::objectValue);
|
|
asset["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
Json::Value lockingChainIssue(Json::objectValue);
|
|
lockingChainIssue["currency"] = "EUR";
|
|
lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["LockingChainIssue"] = lockingChainIssue;
|
|
bridge["IssuingChainIssue"] = asset;
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid XChainBridge: asset missing issuer
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value asset(Json::objectValue);
|
|
asset["currency"] = "USD";
|
|
Json::Value lockingChainIssue(Json::objectValue);
|
|
lockingChainIssue["currency"] = "EUR";
|
|
lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["LockingChainIssue"] = lockingChainIssue;
|
|
bridge["IssuingChainIssue"] = asset;
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid XChainBridge: asset issuer not base58
|
|
{
|
|
Json::Value j;
|
|
Json::Value bridge(Json::objectValue);
|
|
Json::Value asset(Json::objectValue);
|
|
asset["currency"] = "USD";
|
|
asset["issuer"] = "notAValidBase58Account";
|
|
Json::Value lockingChainIssue(Json::objectValue);
|
|
lockingChainIssue["currency"] = "EUR";
|
|
lockingChainIssue["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
|
bridge["LockingChainIssue"] = lockingChainIssue;
|
|
bridge["IssuingChainIssue"] = asset;
|
|
j[sfXChainBridge] = bridge;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid XChainBridge: not an object
|
|
{
|
|
Json::Value j;
|
|
j[sfXChainBridge] = "notanobject";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testNumber()
|
|
{
|
|
testcase("Number");
|
|
// Valid integer value for STNumber
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = 12345;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(12345, 0));
|
|
}
|
|
|
|
// Valid uint value for STNumber
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = 12345u;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(12345, 0));
|
|
}
|
|
|
|
// Valid string integer value for STNumber
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = "67890";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(67890, 0));
|
|
}
|
|
|
|
// Valid negative integer value for STNumber
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = -42;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(-42, 0));
|
|
}
|
|
|
|
// Valid string negative integer value for STNumber
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = "-123";
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(-123, 0));
|
|
}
|
|
|
|
// Valid floating point value for STNumber
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = "3.14159";
|
|
STParsedJSONObject obj("Test", j);
|
|
if (BEAST_EXPECT(obj.object); obj.object.has_value())
|
|
{
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfNumber));
|
|
BEAST_EXPECT(obj.object->getFieldNumber(sfNumber).value() == Number(314159, -5));
|
|
}
|
|
}
|
|
|
|
// Invalid string value for STNumber (not a number)
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = "notanumber";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid array value for STNumber
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = Json::Value(Json::arrayValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Invalid object value for STNumber
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = Json::Value(Json::objectValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Empty string for STNumber (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfNumber] = "";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testObject()
|
|
{
|
|
testcase("Object");
|
|
// Test with valid object for Object
|
|
{
|
|
Json::Value j;
|
|
Json::Value objVal(Json::objectValue);
|
|
objVal[sfTransactionResult] = 1;
|
|
j[sfTransactionMetaData] = objVal;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfTransactionMetaData));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& result = obj.object->peekFieldObject(sfTransactionMetaData);
|
|
BEAST_EXPECT(result.getFieldU8(sfTransactionResult) == 1);
|
|
}
|
|
|
|
// Test with non-object value for Object (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfTransactionMetaData] = "notanobject";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with array value for Object (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
arr.append(1);
|
|
j[sfTransactionMetaData] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with null value for Object (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfTransactionMetaData] = Json::Value(Json::nullValue);
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with max depth (should succeed)
|
|
// max depth is 64
|
|
{
|
|
Json::Value j;
|
|
Json::Value obj(Json::objectValue);
|
|
Json::Value* current = &obj;
|
|
for (int i = 0; i < 63; ++i)
|
|
{
|
|
Json::Value const next(Json::objectValue);
|
|
(*current)[sfTransactionMetaData] = next;
|
|
current = &((*current)[sfTransactionMetaData]);
|
|
}
|
|
(*current)[sfTransactionResult.getJsonName()] = 1;
|
|
j[sfTransactionMetaData] = obj;
|
|
STParsedJSONObject parsed("Test", j);
|
|
BEAST_EXPECT(parsed.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(parsed.object->isFieldPresent(sfTransactionMetaData));
|
|
}
|
|
|
|
// Test with depth exceeding maxDepth (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value obj(Json::objectValue);
|
|
Json::Value* current = &obj;
|
|
for (int i = 0; i < 64; ++i)
|
|
{
|
|
Json::Value const next(Json::objectValue);
|
|
(*current)[sfTransactionMetaData] = next;
|
|
current = &((*current)[sfTransactionMetaData]);
|
|
}
|
|
(*current)[sfTransactionResult.getJsonName()] = 1;
|
|
j[sfTransactionMetaData] = obj;
|
|
STParsedJSONObject const parsed("Test", j);
|
|
BEAST_EXPECT(!parsed.object.has_value());
|
|
}
|
|
}
|
|
|
|
void
|
|
testArray()
|
|
{
|
|
testcase("Array");
|
|
// Test with valid array for Array
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem[sfTransactionResult] = 2;
|
|
Json::Value elem2(Json::objectValue);
|
|
elem2[sfTransactionMetaData] = elem;
|
|
arr.append(elem2);
|
|
j[sfSignerEntries] = arr;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries));
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
auto const& result = obj.object->getFieldArray(sfSignerEntries);
|
|
if (BEAST_EXPECT(result.size() == 1))
|
|
{
|
|
BEAST_EXPECT(result[0].getFName() == sfTransactionMetaData);
|
|
BEAST_EXPECT(result[0].getJson(0) == elem);
|
|
}
|
|
}
|
|
|
|
// Test with array containing non-object element (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
arr.append("notanobject");
|
|
j[sfSignerEntries] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with array containing object with invalid field (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem["invalidField"] = 1;
|
|
arr.append(elem);
|
|
j[sfSignerEntries] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with array containing object with multiple keys (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem[sfTransactionResult] = 2;
|
|
elem[sfNetworkID] = 3;
|
|
arr.append(elem);
|
|
j[sfSignerEntries] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with non-array value for Array (should fail)
|
|
{
|
|
Json::Value j;
|
|
j[sfSignerEntries] = "notanarray";
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with array containing object with valid field but invalid value
|
|
// (should fail)
|
|
{
|
|
Json::Value j;
|
|
Json::Value arr(Json::arrayValue);
|
|
Json::Value elem(Json::objectValue);
|
|
elem[sfTransactionResult] = "notanint";
|
|
arr.append(elem);
|
|
j[sfSignerEntries] = arr;
|
|
STParsedJSONObject const obj("Test", j);
|
|
BEAST_EXPECT(!obj.object.has_value());
|
|
}
|
|
|
|
// Test with empty array for Array (should be valid)
|
|
{
|
|
Json::Value j;
|
|
Json::Value const arr(Json::arrayValue);
|
|
j[sfSignerEntries] = arr;
|
|
STParsedJSONObject obj("Test", j);
|
|
BEAST_EXPECT(obj.object.has_value());
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
BEAST_EXPECT(obj.object->isFieldPresent(sfSignerEntries));
|
|
}
|
|
|
|
// Test with object provided but not object SField
|
|
{
|
|
Json::Value j;
|
|
Json::Value obj(Json::arrayValue);
|
|
obj.append(Json::Value(Json::objectValue));
|
|
obj[0u][sfTransactionResult] = 1;
|
|
j[sfSignerEntries] = obj;
|
|
STParsedJSONObject const parsed("Test", j);
|
|
BEAST_EXPECT(!parsed.object.has_value());
|
|
}
|
|
|
|
// Test invalid children
|
|
{
|
|
try
|
|
{
|
|
/*
|
|
|
|
STArray/STObject constructs don't really map perfectly to json
|
|
arrays/objects.
|
|
|
|
STObject is an associative container, mapping fields to value,
|
|
but an STObject may also have a Field as its name, stored
|
|
outside the associative structure. The name is important, so to
|
|
maintain fidelity, it will take TWO json objects to represent
|
|
them.
|
|
|
|
*/
|
|
std::string const faulty(
|
|
"{\"Template\":[{"
|
|
"\"ModifiedNode\":{\"Sequence\":1}, "
|
|
"\"DeletedNode\":{\"Sequence\":1}"
|
|
"}]}");
|
|
|
|
std::unique_ptr<STObject> const so;
|
|
Json::Value faultyJson;
|
|
bool const parsedOK(parseJSONString(faulty, faultyJson));
|
|
unexpected(!parsedOK, "failed to parse");
|
|
STParsedJSONObject const parsed("test", faultyJson);
|
|
BEAST_EXPECT(!parsed.object);
|
|
}
|
|
catch (std::runtime_error& e)
|
|
{
|
|
std::string const what(e.what());
|
|
unexpected(what.find("First level children of `Template`") != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testEdgeCases()
|
|
{
|
|
testcase("General Invalid Cases");
|
|
|
|
{
|
|
Json::Value j;
|
|
j[sfLedgerEntry] = 1; // not a valid SField for STParsedJSON
|
|
}
|
|
|
|
{
|
|
std::string const goodJson(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransactionResult":"tecFROZEN"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
if (BEAST_EXPECT(parsed.object))
|
|
{
|
|
std::string const& serialized(
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
to_string(parsed.object->getJson(JsonOptions::none)));
|
|
BEAST_EXPECT(serialized == goodJson);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const goodJson(
|
|
R"({"CloseResolution":19,"Method":"250",)"
|
|
R"("TransactionResult":"tecFROZEN"})");
|
|
std::string const expectedJson(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransactionResult":"tecFROZEN"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
|
|
{
|
|
// Integer values are always parsed as int,
|
|
// unless they're too big. We want a small uint.
|
|
jv["CloseResolution"] = Json::UInt(19);
|
|
STParsedJSONObject parsed("test", jv);
|
|
if (BEAST_EXPECT(parsed.object))
|
|
{
|
|
std::string const& serialized(
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
to_string(parsed.object->getJson(JsonOptions::none)));
|
|
BEAST_EXPECT(serialized == expectedJson);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const goodJson(
|
|
R"({"CloseResolution":"19","Method":"250",)"
|
|
R"("TransactionResult":"tecFROZEN"})");
|
|
std::string const expectedJson(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransactionResult":"tecFROZEN"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
|
|
{
|
|
// Integer values are always parsed as int,
|
|
// unless they're too big. We want a small uint.
|
|
jv["CloseResolution"] = Json::UInt(19);
|
|
STParsedJSONObject parsed("test", jv);
|
|
if (BEAST_EXPECT(parsed.object))
|
|
{
|
|
std::string const& serialized(
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
to_string(parsed.object->getJson(JsonOptions::none)));
|
|
BEAST_EXPECT(serialized == expectedJson);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const json(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransactionResult":"terQUEUED"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(json, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
BEAST_EXPECT(!parsed.object);
|
|
BEAST_EXPECT(parsed.error);
|
|
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
|
|
BEAST_EXPECT(
|
|
parsed.error[jss::error_message] ==
|
|
"Field 'test.TransactionResult' is out of range.");
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const json(
|
|
R"({"CloseResolution":19,"Method":"pony",)"
|
|
R"("TransactionResult":"tesSUCCESS"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(json, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
BEAST_EXPECT(!parsed.object);
|
|
BEAST_EXPECT(parsed.error);
|
|
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
|
|
BEAST_EXPECT(
|
|
parsed.error[jss::error_message] == "Field 'test.Method' has bad type.");
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const json(
|
|
R"({"CloseResolution":19,"Method":3294967296,)"
|
|
R"("TransactionResult":"tesSUCCESS"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(json, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
BEAST_EXPECT(!parsed.object);
|
|
BEAST_EXPECT(parsed.error);
|
|
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
|
|
BEAST_EXPECT(
|
|
parsed.error[jss::error_message] == "Field 'test.Method' is out of range.");
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const json(
|
|
R"({"CloseResolution":-10,"Method":42,)"
|
|
R"("TransactionResult":"tesSUCCESS"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(json, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
BEAST_EXPECT(!parsed.object);
|
|
BEAST_EXPECT(parsed.error);
|
|
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
|
|
BEAST_EXPECT(
|
|
parsed.error[jss::error_message] ==
|
|
"Field 'test.CloseResolution' is out of range.");
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const json(
|
|
R"({"CloseResolution":19,"Method":3.141592653,)"
|
|
R"("TransactionResult":"tesSUCCESS"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(json, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
BEAST_EXPECT(!parsed.object);
|
|
BEAST_EXPECT(parsed.error);
|
|
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
|
|
BEAST_EXPECT(
|
|
parsed.error[jss::error_message] == "Field 'test.Method' has bad type.");
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const goodJson(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransferFee":"65535"})");
|
|
std::string const expectedJson(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransferFee":65535})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(goodJson, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
if (BEAST_EXPECT(parsed.object))
|
|
{
|
|
std::string const& serialized(
|
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
|
to_string(parsed.object->getJson(JsonOptions::none)));
|
|
BEAST_EXPECT(serialized == expectedJson);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const json(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransferFee":"65536"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(json, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
BEAST_EXPECT(!parsed.object);
|
|
BEAST_EXPECT(parsed.error);
|
|
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
|
|
BEAST_EXPECT(
|
|
parsed.error[jss::error_message] ==
|
|
"Field 'test.TransferFee' has invalid data.");
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const json(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransferFee":"Payment"})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(json, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
BEAST_EXPECT(!parsed.object);
|
|
BEAST_EXPECT(parsed.error);
|
|
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
|
|
BEAST_EXPECT(
|
|
parsed.error[jss::error_message] ==
|
|
"Field 'test.TransferFee' has invalid data.");
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string const json(
|
|
R"({"CloseResolution":19,"Method":250,)"
|
|
R"("TransferFee":true})");
|
|
|
|
Json::Value jv;
|
|
if (BEAST_EXPECT(parseJSONString(json, jv)))
|
|
{
|
|
STParsedJSONObject parsed("test", jv);
|
|
BEAST_EXPECT(!parsed.object);
|
|
BEAST_EXPECT(parsed.error);
|
|
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
|
|
BEAST_EXPECT(
|
|
parsed.error[jss::error_message] == "Field 'test.TransferFee' has bad type.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
// Instantiate a jtx::Env so debugLog writes are exercised.
|
|
test::jtx::Env const env(*this);
|
|
testUInt8();
|
|
testUInt16();
|
|
testUInt32();
|
|
testUInt64();
|
|
testUInt128();
|
|
testUInt160();
|
|
testUInt192();
|
|
testUInt256();
|
|
testInt32();
|
|
testBlob();
|
|
testVector256();
|
|
testAccount();
|
|
testCurrency();
|
|
testAmount();
|
|
testPathSet();
|
|
testIssue();
|
|
testXChainBridge();
|
|
testNumber();
|
|
testObject();
|
|
testArray();
|
|
testEdgeCases();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(STParsedJSON, protocol, xrpl);
|
|
|
|
} // namespace xrpl
|