From fc4b127ed33fea338f9fb461c31eaaab2ad1da0f Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 4 Sep 2016 23:18:26 -0700 Subject: [PATCH] Add tests for async queue and rpc commands: z_getoperationstatus, z_getoperationresult, z_listoperationids, z_sendmany --- src/test/rpc_wallet_tests.cpp | 185 +++++++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 3 deletions(-) diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index 1d93ed822..f3bccf615 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -17,6 +17,7 @@ #include "asyncrpcqueue.h" #include "asyncrpcoperation.h" #include "wallet/asyncrpcoperation_sendmany.h" +#include "rpcprotocol.h" #include #include @@ -451,10 +452,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) /** - * Testing Async RPC operations. - * + * Test Async RPC operations. * Tip: Create mock operations by subclassing AsyncRPCOperation. */ + class MockSleepOperation : public AsyncRPCOperation { public: std::chrono::milliseconds naptime; @@ -607,7 +608,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_cancel) q->addWorker(); BOOST_CHECK(q->getNumberOfWorkers() == 2); - int numOperations = 10000; + int numOperations = 10000; // 10000 seconds to complete for (int i=0; i op(new CountOperation()); q->addOperation(op); @@ -616,6 +617,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_cancel) BOOST_CHECK(ids.size()==numOperations); q->closeAndWait(); + // the shared counter should equal the number of successful operations. BOOST_CHECK_NE(numOperations, gCounter.load()); int numSuccess = 0; @@ -636,5 +638,182 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_cancel) BOOST_CHECK(ids.size()==0); } +// This tests z_getoperationstatus, z_getoperationresult, z_listoperationids +BOOST_AUTO_TEST_CASE(rpc_z_getoperations) +{ + std::shared_ptr q = getAsyncRPCQueue(); + std::shared_ptr sharedInstance = AsyncRPCQueue::sharedInstance(); + BOOST_CHECK(q == sharedInstance); + + BOOST_CHECK_NO_THROW(CallRPC("z_getoperationstatus")); + BOOST_CHECK_NO_THROW(CallRPC("z_getoperationstatus []")); + BOOST_CHECK_NO_THROW(CallRPC("z_getoperationstatus [\"opid-1234\"]")); + BOOST_CHECK_THROW(CallRPC("z_getoperationstatus [] toomanyargs"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_getoperationstatus not_an_array"), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC("z_getoperationresult")); + BOOST_CHECK_NO_THROW(CallRPC("z_getoperationresult []")); + BOOST_CHECK_NO_THROW(CallRPC("z_getoperationresult [\"opid-1234\"]")); + BOOST_CHECK_THROW(CallRPC("z_getoperationresult [] toomanyargs"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_getoperationresult not_an_array"), runtime_error); + + std::shared_ptr op1 = std::make_shared(); + q->addOperation(op1); + std::shared_ptr op2 = std::make_shared(); + q->addOperation(op2); + + BOOST_CHECK(q->getOperationCount() == 2); + BOOST_CHECK(q->getNumberOfWorkers() == 0); + q->addWorker(); + BOOST_CHECK(q->getNumberOfWorkers() == 1); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + BOOST_CHECK(q->getOperationCount() == 0); + + Value retValue; + BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listoperationids")); + BOOST_CHECK(retValue.get_array().size() == 2); + + BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getoperationstatus")); + Array array = retValue.get_array(); + BOOST_CHECK(array.size() == 2); + + // idempotent + BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getoperationstatus")); + array = retValue.get_array(); + BOOST_CHECK(array.size() == 2); + + for (Value v : array) { + Object obj = v.get_obj(); + Value id = find_value(obj, "id"); + + Value result; + // removes result from internal storage + BOOST_CHECK_NO_THROW(result = CallRPC("z_getoperationresult [\"" + id.get_str() + "\"]")); + Array resultArray = result.get_array(); + BOOST_CHECK(resultArray.size() == 1); + + Object resultObj = resultArray.front().get_obj(); + Value resultId = find_value(resultObj, "id"); + BOOST_CHECK_EQUAL(id.get_str(), resultId.get_str()); + } + + // operations removed + BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getoperationstatus")); + array = retValue.get_array(); + BOOST_CHECK(array.size() == 0); + + q->close(); +} + +BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) +{ + SelectParams(CBaseChainParams::TESTNET); + + LOCK(pwalletMain->cs_wallet); + + BOOST_CHECK_THROW(CallRPC("z_sendmany"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_sendmany toofewargs"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_sendmany too many args here"), runtime_error); + + // bad from address + BOOST_CHECK_THROW(CallRPC("z_sendmany INVALIDmwehwBzEHJTB5hiyxjmVkuFu9CHD2Cojjs []"), runtime_error); + // empty amounts + BOOST_CHECK_THROW(CallRPC("z_sendmany mwehwBzEHJTB5hiyxjmVkuFu9CHD2Cojjs []"), runtime_error); + + // don't have the spending key for this address + BOOST_CHECK_THROW(CallRPC("z_sendmany tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkBUkJ1oSfbhTJhm72WiZizvkZz5aH1 []"), runtime_error); + + // duplicate address + BOOST_CHECK_THROW(CallRPC("z_sendmany mwehwBzEHJTB5hiyxjmVkuFu9CHD2Cojjs [{\"address\":\"mvBkHw3UTeV2ivipmSA6uo8yjN4DqZ5KoG\", \"amount\":50.0}, {\"address\":\"mvBkHw3UTeV2ivipmSA6uo8yjN4DqZ5KoG\", \"amount\":12.0} ]"), runtime_error); + + // memo bigger than allowed length of ZC_MEMO_SIZE + std::vector v (ZC_MEMO_SIZE*2); + std::fill(v.begin(),v.end(), 0xFF); + std::string badmemo(v.begin(), v.end()); + CZCPaymentAddress pa = pwalletMain->GenerateNewZKey(); + std::string zaddr1 = pa.ToString(); + BOOST_CHECK_THROW(CallRPC(string("z_sendmany mwehwBzEHJTB5hiyxjmVkuFu9CHD2Cojjs ") + "[{\"address\":\"" + zaddr1 + "\", \"amount\":123.456}]"), runtime_error); + + // Test constructor of AsyncRPCOperation_sendmany + try { + std::shared_ptr operation(new AsyncRPCOperation_sendmany("",{}, {}, -1)); + } catch (const Object& objError) { + BOOST_CHECK( find_value(objError, "message").get_str().find("Minconf cannot be negative") != string::npos); + } + + try { + std::shared_ptr operation(new AsyncRPCOperation_sendmany("",{}, {}, 1)); + } catch (const Object& objError) { + BOOST_CHECK( find_value(objError, "message").get_str().find("From address parameter missing")!= string::npos); + } + + try { + std::shared_ptr operation( new AsyncRPCOperation_sendmany("mwehwBzEHJTB5hiyxjmVkuFu9CHD2Cojjs", {}, {}, 1) ); + } catch (const Object& objError) { + BOOST_CHECK( find_value(objError, "message").get_str().find("No recipients")!= string::npos); + } + + try { + std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; + std::shared_ptr operation( new AsyncRPCOperation_sendmany("INVALID", recipients, {}, 1) ); + } catch (const Object& objError) { + BOOST_CHECK( find_value(objError, "message").get_str().find("payment address is invalid")!= string::npos); + } + + // This test is for testnet addresses which begin with 't' not 'z'. + try { + std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; + std::shared_ptr operation( new AsyncRPCOperation_sendmany("zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", recipients, {}, 1) ); + } catch (const Object& objError) { + BOOST_CHECK( find_value(objError, "message").get_str().find("payment address is for wrong network type")!= string::npos); + } + + // Note: The following will crash as a google test because AsyncRPCOperation_sendmany + // invokes a method on pwalletMain, which is undefined in the google test environment. + try { + std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; + std::shared_ptr operation( new AsyncRPCOperation_sendmany("tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkBUkJ1oSfbhTJhm72WiZizvkZz5aH1", recipients, {}, 1) ); + } catch (const Object& objError) { + BOOST_CHECK( find_value(objError, "message").get_str().find("no spending key found for zaddr")!= string::npos); + } +} + +// TODO: test private methods +BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) +{ + SelectParams(CBaseChainParams::TESTNET); + + LOCK(pwalletMain->cs_wallet); + + Value retValue; + + // add keys manually + BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress")); + std::string taddr1 = retValue.get_str(); + CZCPaymentAddress pa = pwalletMain->GenerateNewZKey(); + std::string zaddr1 = pa.ToString(); + + // there are no utxos to spend + { + std::vector recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF") }; + std::shared_ptr operation( new AsyncRPCOperation_sendmany(taddr1, {}, recipients, 1) ); + operation->main(); + BOOST_CHECK(operation->isFailed()); + std::string msg = operation->getErrorMessage(); + BOOST_CHECK( msg.find("Insufficient funds, no UTXOs found") != string::npos); + } + + // there are no unspent notes to spend + { + std::vector recipients = { SendManyRecipient(taddr1,100.0, "DEADBEEF") }; + std::shared_ptr operation( new AsyncRPCOperation_sendmany(zaddr1, recipients, {}, 1) ); + operation->main(); + BOOST_CHECK(operation->isFailed()); + std::string msg = operation->getErrorMessage(); + BOOST_CHECK( msg.find("Insufficient funds, no unspent notes") != string::npos); + } + +} + BOOST_AUTO_TEST_SUITE_END()