-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: env unit test RPC errors return a unique result: #4887
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -280,31 +280,39 @@ Env::parseResult(Json::Value const& jr) | |
jr[jss::result].isMember(jss::engine_result_code)) | ||
ter = TER::fromInt(jr[jss::result][jss::engine_result_code].asInt()); | ||
else | ||
ter = temINVALID; | ||
// Use an error code that is not used anywhere in the transaction engine | ||
// to distinguish this case. | ||
ter = telENV_RPC_FAILED; | ||
return std::make_pair(ter, isTesSuccess(ter) || isTecClaim(ter)); | ||
} | ||
|
||
void | ||
Env::submit(JTx const& jt) | ||
{ | ||
bool didApply; | ||
if (jt.stx) | ||
{ | ||
txid_ = jt.stx->getTransactionID(); | ||
Serializer s; | ||
jt.stx->add(s); | ||
auto const jr = rpc("submit", strHex(s.slice())); | ||
auto const jr = [&]() { | ||
if (jt.stx) | ||
{ | ||
txid_ = jt.stx->getTransactionID(); | ||
Serializer s; | ||
jt.stx->add(s); | ||
auto const jr = rpc("submit", strHex(s.slice())); | ||
|
||
std::tie(ter_, didApply) = parseResult(jr); | ||
} | ||
else | ||
{ | ||
// Parsing failed or the JTx is | ||
// otherwise missing the stx field. | ||
ter_ = temMALFORMED; | ||
didApply = false; | ||
} | ||
return postconditions(jt, ter_, didApply); | ||
std::tie(ter_, didApply) = parseResult(jr); | ||
|
||
return jr; | ||
} | ||
else | ||
{ | ||
// Parsing failed or the JTx is | ||
// otherwise missing the stx field. | ||
ter_ = temMALFORMED; | ||
didApply = false; | ||
|
||
return Json::Value(); | ||
} | ||
}(); | ||
return postconditions(jt, ter_, didApply, jr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a useful change, thanks! it saves developers from printing out the suspicious outputs in debug sessions |
||
} | ||
|
||
void | ||
|
@@ -342,11 +350,15 @@ Env::sign_and_submit(JTx const& jt, Json::Value params) | |
|
||
std::tie(ter_, didApply) = parseResult(jr); | ||
|
||
return postconditions(jt, ter_, didApply); | ||
return postconditions(jt, ter_, didApply, jr); | ||
} | ||
|
||
void | ||
Env::postconditions(JTx const& jt, TER ter, bool didApply) | ||
Env::postconditions( | ||
JTx const& jt, | ||
TER ter, | ||
bool didApply, | ||
Json::Value const& jr) | ||
{ | ||
if (jt.ter && | ||
!test.expect( | ||
|
@@ -356,6 +368,8 @@ Env::postconditions(JTx const& jt, TER ter, bool didApply) | |
transHuman(*jt.ter) + ")")) | ||
{ | ||
test.log << pretty(jt.jv) << std::endl; | ||
if (jr) | ||
test.log << pretty(jr) << std::endl; | ||
// Don't check postconditions if | ||
// we didn't get the expected result. | ||
return; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,7 +56,7 @@ class Memo_test : public beast::unit_test::suite | |
JTx memoSize = makeJtxWithMemo(); | ||
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName] | ||
[sfMemoData.jsonName] = std::string(2020, '0'); | ||
env(memoSize, ter(temINVALID)); | ||
env(memoSize, ter(telENV_RPC_FAILED)); | ||
|
||
// This memo is just barely small enough. | ||
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName] | ||
|
@@ -72,23 +72,23 @@ class Memo_test : public beast::unit_test::suite | |
auto& m = mi[sfCreatedNode.jsonName]; // CreatedNode in Memos | ||
m[sfMemoData.jsonName] = "3030303030"; | ||
|
||
env(memoNonMemo, ter(temINVALID)); | ||
env(memoNonMemo, ter(telENV_RPC_FAILED)); | ||
} | ||
{ | ||
// Put an invalid field in a Memo object. | ||
JTx memoExtra = makeJtxWithMemo(); | ||
memoExtra | ||
.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfFlags.jsonName] = | ||
13; | ||
env(memoExtra, ter(temINVALID)); | ||
env(memoExtra, ter(telENV_RPC_FAILED)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To emphasize my point: this test case underscores invalid fields in a memo object. It is more appropriate to return a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My explanation above covers this test case as well. |
||
} | ||
{ | ||
// Put a character that is not allowed in a URL in a MemoType field. | ||
JTx memoBadChar = makeJtxWithMemo(); | ||
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName] | ||
[sfMemoType.jsonName] = | ||
strHex(std::string_view("ONE<INFINITY")); | ||
env(memoBadChar, ter(temINVALID)); | ||
env(memoBadChar, ter(telENV_RPC_FAILED)); | ||
} | ||
{ | ||
// Put a character that is not allowed in a URL in a MemoData field. | ||
|
@@ -105,7 +105,7 @@ class Memo_test : public beast::unit_test::suite | |
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName] | ||
[sfMemoFormat.jsonName] = | ||
strHex(std::string_view("NoBraces{}InURL")); | ||
env(memoBadChar, ter(temINVALID)); | ||
env(memoBadChar, ter(telENV_RPC_FAILED)); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ximinez Only the error code in
Env.cpp
'sEnv::parseResult
should be returningtelENV_RPC_FAILED
. Why are all the other unit tests using this new error code?For instance, if I use duplicate signers in an RPC call to a mainnet validator (non-unit test scenario), I should get
temINVALID
error code, not atelRNV_RPC_FAILED
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent question. The reason is that the RPC layer does some validation before ever invoking the transaction engine. While some of these checks are redundant, catching them early prevents a lot more work from being done later, and so saves time overall. In this example, I think the duplicate signer check is done before any signature verification, which is way more expensive.
Either way, this request is failing on those RPC checks, and so the result has no
TER
code. You can verify this yourself by changing the result to something else (e.g.tesSUCCESS
), and noting that the new log message will show an RPC-level error, and not a transactor-level error.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I understand.
From a code readability point of view, it doesn't help that
telENV_RPC_FAILED
is scattered across a few unit tests (unrelated to the unit testing skeleton code).If performance were not an issue, I'd love to clean up these instances of RPC-level errors. alas ://
thanks for the explanation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You know.... It might not be a bad idea to introduce a new "requirement" parameter, similar to
ter()
for RPC result code values.... Then the test could explicitly specify which kind of RPC failure it's expecting.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potentially something not called
ter()
butrpcErr()
? So if a particular RPC error was expected we could check for that. It might have the side effect of preventing the check fortesSUCCESS
?How hard would it be to code something like that up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello Ed,
You have done a great job! your branch passes all the tests, I don't think I can do better 😅
I have a minor nit about the placement of a comment, you can find it here: https://github.com/ckeshava/rippled/pull/new/minorNit
Having said that, I wanted to discuss three things:
I feel we can avoid using
exception
as a variable name (to avoid confusing withstd::exception
) here - develop...ximinez:rippled:ci/jtx_rpc#diff-19c35fddcebc11f5dd0b6b647315df1340bdddc6f9a79c3a6de4b6a98a43a5a5R49Regarding the presentation of RPC errors, you have structured it as one of two alternatives:
<rpcCode, epcError> or <rpcError, epcExceptionMessage>
. This does capture all the existing unit tests.But I came across three instances where
<rpcCode, epcError, rpcExceptionMessage>
are all returned by rippled. Here are some examples:As part of future work, it would be nice if we can have consistency across all such instances. Would it be too hard to specify an
rpcErrorCode
in all such instances? I don't know.rpc<error_code>
nearly as much as thetem/tec
error codes. A handful of unit tests expectrpc
-related errors. I know that Bronek is working to add more unit tests. I feel such unit tests are useful to detect breaking of Public APIs. What do you think?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome. Thanks! I'll make those changes and open a PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tried to do a PoC for point-2 in my previous comment. Please check the top two commits: ximinez/rippled@ci/jtx_rpc...ckeshava:minorNit
I think it would be nice if all the error cases would return a
jss::error_code
, instead of only returning ajss::error
orjss::error_message
. What do you think?I think this will be a non-trivial amount of work.
The Developer Growth team is working on an OpenAPI specification. I believe it would be useful if we can have a consistent set of error codes -- it would be easier to model the RPC error cases. (This is my read of the situation, I haven't discussed this with the team yet)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it would be a valuable addition, but I think adding new RPC codes is beyond the scope of my change, partly because it could change the RPC API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, that's true