mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 10:35:50 +00:00
test: Add STInteger and STParsedJSON tests (#5726)
This change is to improve code coverage (and to simplify #5720 and #5725); there is otherwise no change in functionality. The change adds basic tests for `STInteger` and `STParsedJSON`, so it becomes easier to test smaller changes to the types, as well as removes `STParsedJSONArray`, since it is not used anywhere (including in Clio).
This commit is contained in:
@@ -74,6 +74,9 @@ public:
|
||||
Permission&
|
||||
operator=(Permission const&) = delete;
|
||||
|
||||
std::optional<std::string>
|
||||
getPermissionName(std::uint32_t const value) const;
|
||||
|
||||
std::optional<std::uint32_t>
|
||||
getGranularValue(std::string const& name) const;
|
||||
|
||||
|
||||
@@ -54,34 +54,6 @@ public:
|
||||
Json::Value error;
|
||||
};
|
||||
|
||||
/** Holds the serialized result of parsing an input JSON array.
|
||||
This does validation and checking on the provided JSON.
|
||||
*/
|
||||
class STParsedJSONArray
|
||||
{
|
||||
public:
|
||||
/** Parses and creates an STParsedJSON array.
|
||||
The result of the parsing is stored in array and error.
|
||||
Exceptions:
|
||||
Does not throw.
|
||||
@param name The name of the JSON field, used in diagnostics.
|
||||
@param json The JSON-RPC to parse.
|
||||
*/
|
||||
STParsedJSONArray(std::string const& name, Json::Value const& json);
|
||||
|
||||
STParsedJSONArray() = delete;
|
||||
STParsedJSONArray(STParsedJSONArray const&) = delete;
|
||||
STParsedJSONArray&
|
||||
operator=(STParsedJSONArray const&) = delete;
|
||||
~STParsedJSONArray() = default;
|
||||
|
||||
/** The STArray if the parse was successful. */
|
||||
std::optional<STArray> array;
|
||||
|
||||
/** On failure, an appropriate set of error values. */
|
||||
Json::Value error;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -101,6 +101,22 @@ Permission::getInstance()
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
Permission::getPermissionName(std::uint32_t const value) const
|
||||
{
|
||||
auto const permissionValue = static_cast<GranularPermissionType>(value);
|
||||
if (auto const granular = getGranularName(permissionValue))
|
||||
return *granular;
|
||||
|
||||
// not a granular permission, check if it maps to a transaction type
|
||||
auto const txType = permissionToTxType(value);
|
||||
if (auto const* item = TxFormats::getInstance().findByType(txType);
|
||||
item != nullptr)
|
||||
return item->getName();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::uint32_t>
|
||||
Permission::getGranularValue(std::string const& name) const
|
||||
{
|
||||
|
||||
@@ -62,8 +62,10 @@ STUInt8::getText() const
|
||||
if (transResultInfo(TER::fromInt(value_), token, human))
|
||||
return human;
|
||||
|
||||
// LCOV_EXCL_START
|
||||
JLOG(debugLog().error())
|
||||
<< "Unknown result code in metadata: " << value_;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return std::to_string(value_);
|
||||
@@ -80,8 +82,10 @@ STUInt8::getJson(JsonOptions) const
|
||||
if (transResultInfo(TER::fromInt(value_), token, human))
|
||||
return token;
|
||||
|
||||
// LCOV_EXCL_START
|
||||
JLOG(debugLog().error())
|
||||
<< "Unknown result code in metadata: " << value_;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return value_;
|
||||
@@ -171,6 +175,13 @@ template <>
|
||||
std::string
|
||||
STUInt32::getText() const
|
||||
{
|
||||
if (getFName() == sfPermissionValue)
|
||||
{
|
||||
auto const permissionName =
|
||||
Permission::getInstance().getPermissionName(value_);
|
||||
if (permissionName)
|
||||
return *permissionName;
|
||||
}
|
||||
return std::to_string(value_);
|
||||
}
|
||||
|
||||
@@ -180,23 +191,10 @@ STUInt32::getJson(JsonOptions) const
|
||||
{
|
||||
if (getFName() == sfPermissionValue)
|
||||
{
|
||||
auto const permissionValue =
|
||||
static_cast<GranularPermissionType>(value_);
|
||||
auto const granular =
|
||||
Permission::getInstance().getGranularName(permissionValue);
|
||||
|
||||
if (granular)
|
||||
{
|
||||
return *granular;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const txType =
|
||||
Permission::getInstance().permissionToTxType(value_);
|
||||
auto item = TxFormats::getInstance().findByType(txType);
|
||||
if (item != nullptr)
|
||||
return item->getName();
|
||||
}
|
||||
auto const permissionName =
|
||||
Permission::getInstance().getPermissionName(value_);
|
||||
if (permissionName)
|
||||
return *permissionName;
|
||||
}
|
||||
|
||||
return value_;
|
||||
|
||||
@@ -83,7 +83,8 @@ constexpr std::
|
||||
return static_cast<U1>(value);
|
||||
}
|
||||
|
||||
static std::string
|
||||
// LCOV_EXCL_START
|
||||
static inline std::string
|
||||
make_name(std::string const& object, std::string const& field)
|
||||
{
|
||||
if (field.empty())
|
||||
@@ -92,7 +93,7 @@ make_name(std::string const& object, std::string const& field)
|
||||
return object + "." + field;
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
not_an_object(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -100,20 +101,20 @@ not_an_object(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' is not a JSON object.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
not_an_object(std::string const& object)
|
||||
{
|
||||
return not_an_object(object, "");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
not_an_array(std::string const& object)
|
||||
{
|
||||
return RPC::make_error(
|
||||
rpcINVALID_PARAMS, "Field '" + object + "' is not a JSON array.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
unknown_field(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -121,7 +122,7 @@ unknown_field(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' is unknown.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
out_of_range(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -129,7 +130,7 @@ out_of_range(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' is out of range.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
bad_type(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -137,7 +138,7 @@ bad_type(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' has bad type.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
invalid_data(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -145,13 +146,13 @@ invalid_data(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' has invalid data.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
invalid_data(std::string const& object)
|
||||
{
|
||||
return invalid_data(object, "");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
array_expected(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -159,7 +160,7 @@ array_expected(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' must be a JSON array.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
string_expected(std::string const& object, std::string const& field)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -167,7 +168,7 @@ string_expected(std::string const& object, std::string const& field)
|
||||
"Field '" + make_name(object, field) + "' must be a string.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
too_deep(std::string const& object)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -175,7 +176,7 @@ too_deep(std::string const& object)
|
||||
"Field '" + object + "' exceeds nesting depth limit.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
singleton_expected(std::string const& object, unsigned int index)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -184,7 +185,7 @@ singleton_expected(std::string const& object, unsigned int index)
|
||||
"]' must be an object with a single key/object value.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
template_mismatch(SField const& sField)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -193,7 +194,7 @@ template_mismatch(SField const& sField)
|
||||
"' contents did not meet requirements for that type.");
|
||||
}
|
||||
|
||||
static Json::Value
|
||||
static inline Json::Value
|
||||
non_object_in_array(std::string const& item, Json::UInt index)
|
||||
{
|
||||
return RPC::make_error(
|
||||
@@ -201,6 +202,7 @@ non_object_in_array(std::string const& item, Json::UInt index)
|
||||
"Item '" + item + "' at index " + std::to_string(index) +
|
||||
" is not an object. Arrays may only contain objects.");
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
template <class STResult, class Integer>
|
||||
static std::optional<detail::STVar>
|
||||
@@ -385,10 +387,13 @@ parseLeaf(
|
||||
|
||||
auto const& field = SField::getField(fieldName);
|
||||
|
||||
// checked in parseObject
|
||||
if (field == sfInvalid)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
error = unknown_field(json_name, fieldName);
|
||||
return ret;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
switch (field.fieldType)
|
||||
@@ -760,6 +765,12 @@ parseLeaf(
|
||||
AccountID uAccount, uIssuer;
|
||||
Currency uCurrency;
|
||||
|
||||
if (!account && !currency && !issuer)
|
||||
{
|
||||
error = invalid_data(element_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (account)
|
||||
{
|
||||
// human account id
|
||||
@@ -1153,24 +1164,4 @@ STParsedJSONObject::STParsedJSONObject(
|
||||
object = parseObject(name, json, sfGeneric, 0, error);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
STParsedJSONArray::STParsedJSONArray(
|
||||
std::string const& name,
|
||||
Json::Value const& json)
|
||||
{
|
||||
using namespace STParsedJSONDetail;
|
||||
auto arr = parseArray(name, json, sfGeneric, 0, error);
|
||||
if (!arr)
|
||||
array.reset();
|
||||
else
|
||||
{
|
||||
auto p = dynamic_cast<STArray*>(&arr->get());
|
||||
if (p == nullptr)
|
||||
array.reset();
|
||||
else
|
||||
array = std::move(*p);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
135
src/test/protocol/STInteger_test.cpp
Normal file
135
src/test/protocol/STInteger_test.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2025 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
#include <xrpl/protocol/STInteger.h>
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
struct STInteger_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
testUInt8()
|
||||
{
|
||||
STUInt8 u8(255);
|
||||
BEAST_EXPECT(u8.value() == 255);
|
||||
BEAST_EXPECT(u8.getText() == "255");
|
||||
BEAST_EXPECT(u8.getSType() == STI_UINT8);
|
||||
BEAST_EXPECT(u8.getJson(JsonOptions::none) == 255);
|
||||
|
||||
// there is some special handling for sfTransactionResult
|
||||
STUInt8 tr(sfTransactionResult, 0);
|
||||
BEAST_EXPECT(tr.value() == 0);
|
||||
BEAST_EXPECT(
|
||||
tr.getText() ==
|
||||
"The transaction was applied. Only final in a validated ledger.");
|
||||
BEAST_EXPECT(tr.getSType() == STI_UINT8);
|
||||
BEAST_EXPECT(tr.getJson(JsonOptions::none) == "tesSUCCESS");
|
||||
|
||||
// invalid transaction result
|
||||
STUInt8 tr2(sfTransactionResult, 255);
|
||||
BEAST_EXPECT(tr2.value() == 255);
|
||||
BEAST_EXPECT(tr2.getText() == "255");
|
||||
BEAST_EXPECT(tr2.getSType() == STI_UINT8);
|
||||
BEAST_EXPECT(tr2.getJson(JsonOptions::none) == 255);
|
||||
}
|
||||
|
||||
void
|
||||
testUInt16()
|
||||
{
|
||||
STUInt16 u16(65535);
|
||||
BEAST_EXPECT(u16.value() == 65535);
|
||||
BEAST_EXPECT(u16.getText() == "65535");
|
||||
BEAST_EXPECT(u16.getSType() == STI_UINT16);
|
||||
BEAST_EXPECT(u16.getJson(JsonOptions::none) == 65535);
|
||||
|
||||
// there is some special handling for sfLedgerEntryType
|
||||
STUInt16 let(sfLedgerEntryType, ltACCOUNT_ROOT);
|
||||
BEAST_EXPECT(let.value() == ltACCOUNT_ROOT);
|
||||
BEAST_EXPECT(let.getText() == "AccountRoot");
|
||||
BEAST_EXPECT(let.getSType() == STI_UINT16);
|
||||
BEAST_EXPECT(let.getJson(JsonOptions::none) == "AccountRoot");
|
||||
|
||||
// there is some special handling for sfTransactionType
|
||||
STUInt16 tlt(sfTransactionType, ttPAYMENT);
|
||||
BEAST_EXPECT(tlt.value() == ttPAYMENT);
|
||||
BEAST_EXPECT(tlt.getText() == "Payment");
|
||||
BEAST_EXPECT(tlt.getSType() == STI_UINT16);
|
||||
BEAST_EXPECT(tlt.getJson(JsonOptions::none) == "Payment");
|
||||
}
|
||||
|
||||
void
|
||||
testUInt32()
|
||||
{
|
||||
STUInt32 u32(4'294'967'295u);
|
||||
BEAST_EXPECT(u32.value() == 4'294'967'295u);
|
||||
BEAST_EXPECT(u32.getText() == "4294967295");
|
||||
BEAST_EXPECT(u32.getSType() == STI_UINT32);
|
||||
BEAST_EXPECT(u32.getJson(JsonOptions::none) == 4'294'967'295u);
|
||||
|
||||
// there is some special handling for sfPermissionValue
|
||||
STUInt32 pv(sfPermissionValue, ttPAYMENT + 1);
|
||||
BEAST_EXPECT(pv.value() == ttPAYMENT + 1);
|
||||
BEAST_EXPECT(pv.getText() == "Payment");
|
||||
BEAST_EXPECT(pv.getSType() == STI_UINT32);
|
||||
BEAST_EXPECT(pv.getJson(JsonOptions::none) == "Payment");
|
||||
STUInt32 pv2(sfPermissionValue, PaymentMint);
|
||||
BEAST_EXPECT(pv2.value() == PaymentMint);
|
||||
BEAST_EXPECT(pv2.getText() == "PaymentMint");
|
||||
BEAST_EXPECT(pv2.getSType() == STI_UINT32);
|
||||
BEAST_EXPECT(pv2.getJson(JsonOptions::none) == "PaymentMint");
|
||||
}
|
||||
|
||||
void
|
||||
testUInt64()
|
||||
{
|
||||
STUInt64 u64(0xFFFFFFFFFFFFFFFFull);
|
||||
BEAST_EXPECT(u64.value() == 0xFFFFFFFFFFFFFFFFull);
|
||||
BEAST_EXPECT(u64.getText() == "18446744073709551615");
|
||||
BEAST_EXPECT(u64.getSType() == STI_UINT64);
|
||||
|
||||
// By default, getJson returns hex string
|
||||
auto jsonVal = u64.getJson(JsonOptions::none);
|
||||
BEAST_EXPECT(jsonVal.isString());
|
||||
BEAST_EXPECT(jsonVal.asString() == "ffffffffffffffff");
|
||||
|
||||
STUInt64 u64_2(sfMaximumAmount, 0xFFFFFFFFFFFFFFFFull);
|
||||
BEAST_EXPECT(u64_2.value() == 0xFFFFFFFFFFFFFFFFull);
|
||||
BEAST_EXPECT(u64_2.getText() == "18446744073709551615");
|
||||
BEAST_EXPECT(u64_2.getSType() == STI_UINT64);
|
||||
BEAST_EXPECT(
|
||||
u64_2.getJson(JsonOptions::none) == "18446744073709551615");
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testUInt8();
|
||||
testUInt16();
|
||||
testUInt32();
|
||||
testUInt64();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(STInteger, protocol, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user