mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-27 22:45:52 +00:00
Add test for feature RPC (RIPD-1391):
Create unit test for feature RPC method. Add client_error field to env RPC requests to provide information about parsing errors.
This commit is contained in:
committed by
Nik Bougalis
parent
aea54b7230
commit
7b82051bdb
@@ -4863,6 +4863,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\test\rpc\Feature_test.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\test\rpc\GatewayBalances_test.cpp">
|
<ClCompile Include="..\..\src\test\rpc\GatewayBalances_test.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -5565,6 +5565,9 @@
|
|||||||
<ClCompile Include="..\..\src\test\rpc\Book_test.cpp">
|
<ClCompile Include="..\..\src\test\rpc\Book_test.cpp">
|
||||||
<Filter>test\rpc</Filter>
|
<Filter>test\rpc</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\test\rpc\Feature_test.cpp">
|
||||||
|
<Filter>test\rpc</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\test\rpc\GatewayBalances_test.cpp">
|
<ClCompile Include="..\..\src\test\rpc\GatewayBalances_test.cpp">
|
||||||
<Filter>test\rpc</Filter>
|
<Filter>test\rpc</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -517,13 +517,23 @@ Env::do_rpc(std::vector<std::string> const& args)
|
|||||||
jv[jss::ripplerpc] = "2.0";
|
jv[jss::ripplerpc] = "2.0";
|
||||||
jv[jss::id] = 5;
|
jv[jss::id] = 5;
|
||||||
}
|
}
|
||||||
auto response = client().invoke(jv[jss::method].asString(), jv[jss::params][0U]);
|
auto response = client().invoke(
|
||||||
|
jv[jss::method].asString(),
|
||||||
|
jv[jss::params][0U]);
|
||||||
|
|
||||||
if (jv.isMember(jss::jsonrpc))
|
if (jv.isMember(jss::jsonrpc))
|
||||||
{
|
{
|
||||||
response[jss::jsonrpc] = jv[jss::jsonrpc];
|
response[jss::jsonrpc] = jv[jss::jsonrpc];
|
||||||
response[jss::ripplerpc] = jv[jss::ripplerpc];
|
response[jss::ripplerpc] = jv[jss::ripplerpc];
|
||||||
response[jss::id] = jv[jss::id];
|
response[jss::id] = jv[jss::id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (jv[jss::params][0u].isMember(jss::error) &&
|
||||||
|
(! response.isMember(jss::error)))
|
||||||
|
{
|
||||||
|
response["client_error"] = jv[jss::params][0u];
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
283
src/test/rpc/Feature_test.cpp
Normal file
283
src/test/rpc/Feature_test.cpp
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012-2017 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 <BeastConfig.h>
|
||||||
|
#include <test/jtx.h>
|
||||||
|
#include <ripple/protocol/Feature.h>
|
||||||
|
#include <ripple/protocol/JsonFields.h>
|
||||||
|
#include <ripple/app/misc/AmendmentTable.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
class Feature_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
testNoParams()
|
||||||
|
{
|
||||||
|
testcase ("No Params, None Enabled");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
|
||||||
|
auto jrr = env.rpc("feature") [jss::result];
|
||||||
|
if(! BEAST_EXPECT(jrr.isMember(jss::features)))
|
||||||
|
return;
|
||||||
|
for(auto const& feature : jrr[jss::features])
|
||||||
|
{
|
||||||
|
if(! BEAST_EXPECT(feature.isMember(jss::name)))
|
||||||
|
return;
|
||||||
|
//default config - so all should be disabled, not vetoed, and supported
|
||||||
|
BEAST_EXPECTS(! feature[jss::enabled].asBool(),
|
||||||
|
feature[jss::name].asString() + " enabled");
|
||||||
|
BEAST_EXPECTS(! feature[jss::vetoed].asBool(),
|
||||||
|
feature[jss::name].asString() + " vetoed");
|
||||||
|
BEAST_EXPECTS(feature[jss::supported].asBool(),
|
||||||
|
feature[jss::name].asString() + " supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testSingleFeature()
|
||||||
|
{
|
||||||
|
testcase ("Feature Param");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
|
||||||
|
auto jrr = env.rpc("feature", "CryptoConditions") [jss::result];
|
||||||
|
BEAST_EXPECTS(jrr[jss::status] == jss::success, "status");
|
||||||
|
jrr.removeMember(jss::status);
|
||||||
|
BEAST_EXPECT(jrr.size() == 1);
|
||||||
|
auto feature = *(jrr.begin());
|
||||||
|
|
||||||
|
BEAST_EXPECTS(feature[jss::name] == "CryptoConditions", "name");
|
||||||
|
BEAST_EXPECTS(! feature[jss::enabled].asBool(), "enabled");
|
||||||
|
BEAST_EXPECTS(! feature[jss::vetoed].asBool(), "vetoed");
|
||||||
|
BEAST_EXPECTS(feature[jss::supported].asBool(), "supported");
|
||||||
|
|
||||||
|
// feature names are case-sensitive - expect error here
|
||||||
|
jrr = env.rpc("feature", "cryptoconditions") [jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::error] == "badFeature");
|
||||||
|
BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testInvalidFeature()
|
||||||
|
{
|
||||||
|
testcase ("Invalid Feature");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
|
||||||
|
auto jrr = env.rpc("feature", "AllTheThings") [jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::error] == "badFeature");
|
||||||
|
BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testNonAdmin()
|
||||||
|
{
|
||||||
|
testcase ("Feature Without Admin");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this, envconfig([](std::unique_ptr<Config> cfg) {
|
||||||
|
(*cfg)["port_rpc"].set("admin","");
|
||||||
|
(*cfg)["port_ws"].set("admin","");
|
||||||
|
return cfg;
|
||||||
|
})};
|
||||||
|
|
||||||
|
auto jrr = env.rpc("feature") [jss::result];
|
||||||
|
// The current HTTP/S ServerHandler returns an HTTP 403 error code here
|
||||||
|
// rather than a noPermission JSON error. The JSONRPCClient just eats that
|
||||||
|
// error and returns an null result.
|
||||||
|
BEAST_EXPECT(jrr.isNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testSomeEnabled()
|
||||||
|
{
|
||||||
|
testcase ("No Params, Some Enabled");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this,
|
||||||
|
features(featureEscrow),
|
||||||
|
features(featureCryptoConditions)};
|
||||||
|
// The amendment table has to be modified
|
||||||
|
// since that is what feature RPC actually checks
|
||||||
|
env.app().getAmendmentTable().enable(featureEscrow);
|
||||||
|
env.app().getAmendmentTable().enable(featureCryptoConditions);
|
||||||
|
|
||||||
|
auto jrr = env.rpc("feature") [jss::result];
|
||||||
|
if(! BEAST_EXPECT(jrr.isMember(jss::features)))
|
||||||
|
return;
|
||||||
|
for(auto it = jrr[jss::features].begin();
|
||||||
|
it != jrr[jss::features].end(); ++it)
|
||||||
|
{
|
||||||
|
uint256 id;
|
||||||
|
id.SetHexExact(it.key().asString().c_str());
|
||||||
|
if(! BEAST_EXPECT((*it).isMember(jss::name)))
|
||||||
|
return;
|
||||||
|
bool expectEnabled = env.app().getAmendmentTable().isEnabled(id);
|
||||||
|
bool expectSupported = env.app().getAmendmentTable().isSupported(id);
|
||||||
|
BEAST_EXPECTS((*it)[jss::enabled].asBool() == expectEnabled,
|
||||||
|
(*it)[jss::name].asString() + " enabled");
|
||||||
|
BEAST_EXPECTS(! (*it)[jss::vetoed].asBool(),
|
||||||
|
(*it)[jss::name].asString() + " vetoed");
|
||||||
|
BEAST_EXPECTS((*it)[jss::supported].asBool() == expectSupported,
|
||||||
|
(*it)[jss::name].asString() + " supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testWithMajorities()
|
||||||
|
{
|
||||||
|
testcase ("With Majorities");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this, envconfig(validator, "")};
|
||||||
|
|
||||||
|
auto jrr = env.rpc("feature") [jss::result];
|
||||||
|
if(! BEAST_EXPECT(jrr.isMember(jss::features)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// at this point, there are no majorities so no fields related to
|
||||||
|
// amendment voting
|
||||||
|
for(auto const& feature : jrr[jss::features])
|
||||||
|
{
|
||||||
|
if(! BEAST_EXPECT(feature.isMember(jss::name)))
|
||||||
|
return;
|
||||||
|
BEAST_EXPECTS(! feature.isMember(jss::majority),
|
||||||
|
feature[jss::name].asString() + " majority");
|
||||||
|
BEAST_EXPECTS(! feature.isMember(jss::count),
|
||||||
|
feature[jss::name].asString() + " count");
|
||||||
|
BEAST_EXPECTS(! feature.isMember(jss::threshold),
|
||||||
|
feature[jss::name].asString() + " threshold");
|
||||||
|
BEAST_EXPECTS(! feature.isMember(jss::validations),
|
||||||
|
feature[jss::name].asString() + " validations");
|
||||||
|
BEAST_EXPECTS(! feature.isMember(jss::vote),
|
||||||
|
feature[jss::name].asString() + " vote");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto majorities = getMajorityAmendments (*env.closed());
|
||||||
|
if(! BEAST_EXPECT(majorities.empty()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// close ledgers until the amendments show up.
|
||||||
|
for (auto i = 0; i <= 256; ++i)
|
||||||
|
{
|
||||||
|
env.close();
|
||||||
|
majorities = getMajorityAmendments (*env.closed());
|
||||||
|
if (! majorities.empty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There should be at least 5 amendments. Don't do exact comparison
|
||||||
|
// to avoid maintenance as more amendments are added in the future.
|
||||||
|
BEAST_EXPECT(majorities.size() >= 5);
|
||||||
|
|
||||||
|
jrr = env.rpc("feature") [jss::result];
|
||||||
|
if(! BEAST_EXPECT(jrr.isMember(jss::features)))
|
||||||
|
return;
|
||||||
|
for(auto const& feature : jrr[jss::features])
|
||||||
|
{
|
||||||
|
if(! BEAST_EXPECT(feature.isMember(jss::name)))
|
||||||
|
return;
|
||||||
|
BEAST_EXPECTS(feature.isMember(jss::majority),
|
||||||
|
feature[jss::name].asString() + " majority");
|
||||||
|
BEAST_EXPECTS(feature.isMember(jss::count),
|
||||||
|
feature[jss::name].asString() + " count");
|
||||||
|
BEAST_EXPECTS(feature.isMember(jss::threshold),
|
||||||
|
feature[jss::name].asString() + " threshold");
|
||||||
|
BEAST_EXPECTS(feature.isMember(jss::validations),
|
||||||
|
feature[jss::name].asString() + " validations");
|
||||||
|
BEAST_EXPECTS(feature.isMember(jss::vote),
|
||||||
|
feature[jss::name].asString() + " vote");
|
||||||
|
BEAST_EXPECT(feature[jss::vote] == 256);
|
||||||
|
BEAST_EXPECT(feature[jss::majority] == 2740);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testVeto()
|
||||||
|
{
|
||||||
|
testcase ("Veto");
|
||||||
|
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this,
|
||||||
|
features(featureCryptoConditions)};
|
||||||
|
// The amendment table has to be modified
|
||||||
|
// since that is what feature RPC actually checks
|
||||||
|
env.app().getAmendmentTable().enable(featureCryptoConditions);
|
||||||
|
|
||||||
|
auto jrr = env.rpc("feature", "CryptoConditions") [jss::result];
|
||||||
|
if(! BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
|
||||||
|
return;
|
||||||
|
jrr.removeMember(jss::status);
|
||||||
|
if(! BEAST_EXPECT(jrr.size() == 1))
|
||||||
|
return;
|
||||||
|
auto feature = *(jrr.begin());
|
||||||
|
BEAST_EXPECTS(feature[jss::name] == "CryptoConditions", "name");
|
||||||
|
BEAST_EXPECTS(! feature[jss::vetoed].asBool(), "vetoed");
|
||||||
|
|
||||||
|
jrr = env.rpc("feature", "CryptoConditions", "reject") [jss::result];
|
||||||
|
if(! BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
|
||||||
|
return;
|
||||||
|
jrr.removeMember(jss::status);
|
||||||
|
if(! BEAST_EXPECT(jrr.size() == 1))
|
||||||
|
return;
|
||||||
|
feature = *(jrr.begin());
|
||||||
|
BEAST_EXPECTS(feature[jss::name] == "CryptoConditions", "name");
|
||||||
|
BEAST_EXPECTS(feature[jss::vetoed].asBool(), "vetoed");
|
||||||
|
|
||||||
|
jrr = env.rpc("feature", "CryptoConditions", "accept") [jss::result];
|
||||||
|
if(! BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
|
||||||
|
return;
|
||||||
|
jrr.removeMember(jss::status);
|
||||||
|
if(! BEAST_EXPECT(jrr.size() == 1))
|
||||||
|
return;
|
||||||
|
feature = *(jrr.begin());
|
||||||
|
BEAST_EXPECTS(feature[jss::name] == "CryptoConditions", "name");
|
||||||
|
BEAST_EXPECTS(! feature[jss::vetoed].asBool(), "vetoed");
|
||||||
|
|
||||||
|
// anything other than accept or reject is an error
|
||||||
|
jrr = env.rpc("feature", "CryptoConditions", "maybe");
|
||||||
|
if(! BEAST_EXPECT(jrr.isMember("client_error")))
|
||||||
|
return;
|
||||||
|
BEAST_EXPECT(jrr["client_error"][jss::error] == "invalidParams");
|
||||||
|
BEAST_EXPECT(jrr["client_error"][jss::error_message] == "Invalid parameters.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
testNoParams();
|
||||||
|
testSingleFeature();
|
||||||
|
testInvalidFeature();
|
||||||
|
testNonAdmin();
|
||||||
|
testSomeEnabled();
|
||||||
|
testWithMajorities();
|
||||||
|
testVeto();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(Feature,rpc,ripple);
|
||||||
|
|
||||||
|
} // ripple
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <test/rpc/AccountOffers_test.cpp>
|
#include <test/rpc/AccountOffers_test.cpp>
|
||||||
#include <test/rpc/AccountSet_test.cpp>
|
#include <test/rpc/AccountSet_test.cpp>
|
||||||
#include <test/rpc/Book_test.cpp>
|
#include <test/rpc/Book_test.cpp>
|
||||||
|
#include <test/rpc/Feature_test.cpp>
|
||||||
#include <test/rpc/GatewayBalances_test.cpp>
|
#include <test/rpc/GatewayBalances_test.cpp>
|
||||||
#include <test/rpc/GetCounts_test.cpp>
|
#include <test/rpc/GetCounts_test.cpp>
|
||||||
#include <test/rpc/JSONRPC_test.cpp>
|
#include <test/rpc/JSONRPC_test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user