-
Notifications
You must be signed in to change notification settings - Fork 276
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Mikhail Boldyrev <miboldyrev@gmail.com>
- Loading branch information
Showing
6 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Copyright Soramitsu Co., Ltd. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
add_library(executor_fixture executor_fixture.cpp) | ||
target_link_libraries(executor_fixture | ||
executor_itf | ||
) | ||
|
||
add_library(executor_fixture_param executor_fixture_param.cpp) | ||
target_link_libraries(executor_fixture_param | ||
gtest::main | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
/** | ||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "integration/executor/executor_fixture.hpp" | ||
|
||
#include <gtest/gtest.h> | ||
#include <boost/range/adaptor/transformed.hpp> | ||
#include "cryptography/crypto_provider/crypto_defaults.hpp" | ||
#include "framework/common_constants.hpp" | ||
#include "framework/result_gtest_checkers.hpp" | ||
#include "interfaces/query_responses/query_response.hpp" | ||
#include "interfaces/query_responses/signatories_response.hpp" | ||
#include "module/shared_model/mock_objects_factories/mock_command_factory.hpp" | ||
#include "module/shared_model/mock_objects_factories/mock_query_factory.hpp" | ||
|
||
using namespace common_constants; | ||
using namespace executor_testing; | ||
using namespace framework::expected; | ||
using namespace iroha::ametsuchi; | ||
using namespace iroha::expected; | ||
using namespace iroha::integration_framework; | ||
using namespace shared_model::interface::types; | ||
|
||
namespace executor_testing { | ||
|
||
void checkCommandError(const CommandResult &command_result, | ||
CommandError::ErrorCodeType error_code) { | ||
if (auto err = resultToOptionalError(command_result)) { | ||
EXPECT_EQ(err->error_code, error_code); | ||
} else { | ||
ADD_FAILURE() << "Did not get the expected command error!"; | ||
} | ||
} | ||
|
||
std::pair<std::string, std::string> splitNameAndDomain(const std::string &id, | ||
char delimeter) { | ||
auto it = id.find(delimeter); | ||
if (it == std::string::npos) { | ||
throw std::runtime_error(std::string{"Failed to split '"} + id + "' by '" | ||
+ delimeter + "' because delimeter not found."); | ||
} | ||
if (id.find(delimeter, it + 1) != std::string::npos) { | ||
throw std::runtime_error(std::string{"Failed to split '"} + id + "' by '" | ||
+ delimeter | ||
+ "' because delimeter found more than once."); | ||
} | ||
return std::make_pair(id.substr(0, it), id.substr(it + 1)); | ||
} | ||
|
||
std::pair<std::string, std::string> splitAssetId(const std::string &id) { | ||
return splitNameAndDomain(id, '#'); | ||
} | ||
|
||
std::pair<std::string, std::string> splitAccountId(const std::string &id) { | ||
return splitNameAndDomain(id, '@'); | ||
} | ||
|
||
} // namespace executor_testing | ||
|
||
void ExecutorTestBase::SetUp() { | ||
getBackendParam()->clearBackendState(); | ||
auto executor_itf_result = | ||
ExecutorItf::create(getBackendParam()->getExecutorItfParam()); | ||
assertResultValue(executor_itf_result); | ||
executor_itf_ = | ||
resultToOptionalValue((std::move(executor_itf_result))).value(); | ||
} | ||
|
||
ExecutorItf &ExecutorTestBase::getItf() const { | ||
return *executor_itf_; | ||
} | ||
|
||
void ExecutorTestBase::createAsset(const std::string &name, | ||
const std::string &domain, | ||
PrecisionType precision) const { | ||
SCOPED_TRACE("createAsset"); | ||
assertResultValue(getItf().executeMaintenanceCommand( | ||
*getItf().getMockCommandFactory()->constructCreateAsset( | ||
name, domain, precision))); | ||
} | ||
|
||
void ExecutorTestBase::addAsset( | ||
const AccountIdType &dest_account_id, | ||
const AssetIdType &asset_id, | ||
const shared_model::interface::Amount &quantity) { | ||
SCOPED_TRACE("addAsset"); | ||
assertResultValue(getItf().executeMaintenanceCommand( | ||
*getItf().getMockCommandFactory()->constructAddAssetQuantity(asset_id, | ||
quantity))); | ||
assertResultValue(getItf().executeMaintenanceCommand( | ||
*getItf().getMockCommandFactory()->constructTransferAsset( | ||
kAdminId, dest_account_id, asset_id, "adding asset", quantity))); | ||
} | ||
|
||
void ExecutorTestBase::checkAssetQuantities( | ||
const AccountAssetCollectionType &test_quantities, | ||
const std::vector<AssetQuantity> &reference_quantities) { | ||
static const auto make_asset_matcher = [](AssetQuantity reference) { | ||
return ::testing::Truly( | ||
[reference](const shared_model::interface::AccountAsset &tested) { | ||
if (tested.assetId() == reference.asset_id) { | ||
EXPECT_EQ(tested.balance(), reference.balance) | ||
<< "Wrong balance of asset " << reference.asset_id; | ||
return true; | ||
} | ||
return false; | ||
}); | ||
}; | ||
|
||
auto asset_matchers = boost::copy_range< | ||
std::vector<decltype(make_asset_matcher(reference_quantities.front()))>>( | ||
reference_quantities | boost::adaptors::transformed(make_asset_matcher)); | ||
|
||
EXPECT_THAT(test_quantities, | ||
::testing::UnorderedElementsAreArray(asset_matchers)); | ||
} | ||
|
||
void ExecutorTestBase::checkAssetQuantities( | ||
const std::string &account_id, | ||
const std::vector<AssetQuantity> &quantities) { | ||
auto pagination_meta = | ||
getItf().getMockQueryFactory()->constructAssetPaginationMeta( | ||
quantities.size(), boost::none); | ||
getItf() | ||
.executeQueryAndConvertResult( | ||
*getItf().getMockQueryFactory()->constructGetAccountAssets( | ||
account_id, *pagination_meta)) | ||
.specific_response.match( | ||
[&](const auto &get_account_assets_response) { | ||
checkAssetQuantities( | ||
get_account_assets_response.value.accountAssets(), quantities); | ||
}, | ||
[](const auto &other_response) { | ||
ADD_FAILURE() << "Unexpected query response: " | ||
<< other_response.error->toString(); | ||
}); | ||
} | ||
|
||
void ExecutorTestBase::checkSignatories( | ||
const std::string &account_id, | ||
const std::vector<shared_model::crypto::PublicKey> &keys) { | ||
getItf() | ||
.executeQueryAndConvertResult( | ||
*getItf().getMockQueryFactory()->constructGetSignatories(account_id)) | ||
.specific_response.match( | ||
[&](const auto &get_signatories_response) { | ||
EXPECT_THAT(get_signatories_response.value.keys(), | ||
::testing::UnorderedElementsAreArray(keys)); | ||
}, | ||
[](const auto &other_response) { | ||
ADD_FAILURE() << "Unexpected query response: " | ||
<< other_response.error->toString(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/** | ||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#ifndef TEST_INTEGRATION_EXECUTOR_FIXTURE_HPP | ||
#define TEST_INTEGRATION_EXECUTOR_FIXTURE_HPP | ||
|
||
#include "framework/executor_itf/executor_itf.hpp" | ||
|
||
#include <gtest/gtest.h> | ||
#include "common/result.hpp" | ||
#include "cryptography/crypto_provider/crypto_defaults.hpp" | ||
#include "framework/common_constants.hpp" | ||
#include "framework/result_gtest_checkers.hpp" | ||
#include "integration/executor/executor_fixture_param.hpp" | ||
#include "interfaces/query_responses/error_query_response.hpp" | ||
#include "module/shared_model/mock_objects_factories/mock_command_factory.hpp" | ||
#include "module/shared_model/mock_objects_factories/mock_query_factory.hpp" | ||
|
||
namespace executor_testing { | ||
|
||
namespace error_codes { | ||
using shared_model::interface::ErrorQueryResponse; | ||
|
||
// TODO [IR-1816] Akvinikym 06.12.18: remove these constants after | ||
// introducing a uniform way to use them in code | ||
static constexpr ErrorQueryResponse::ErrorCodeType kNoStatefulError = 0; | ||
static constexpr ErrorQueryResponse::ErrorCodeType kNoPermissions = 2; | ||
static constexpr ErrorQueryResponse::ErrorCodeType kInvalidPagination = 4; | ||
static constexpr ErrorQueryResponse::ErrorCodeType kInvalidAccountId = 5; | ||
static constexpr ErrorQueryResponse::ErrorCodeType kInvalidAssetId = 6; | ||
static constexpr ErrorQueryResponse::ErrorCodeType kInvalidHeight = 3; | ||
} // namespace error_codes | ||
|
||
std::pair<std::string, std::string> splitAssetId(const std::string &id); | ||
|
||
std::pair<std::string, std::string> splitAccountId(const std::string &id); | ||
|
||
/** | ||
* Check that general query response contains a specific result type and | ||
* execute a callback on it. | ||
* @tparam SpecificQueryResponse - Expected specific query response. | ||
* @tparam Callback - Type of callback. | ||
* @param response - The response to be checked. | ||
* @param callback - The callback to be executed on specific result. | ||
*/ | ||
template <typename SpecificQueryResponse, typename Callback> | ||
void checkSuccessfulResult( | ||
const iroha::ametsuchi::QueryExecutorResult &response, | ||
Callback callback) { | ||
auto specific_result = | ||
boost::strict_get<const SpecificQueryResponse &>(&response->get()); | ||
if (not specific_result) { | ||
ADD_FAILURE() << "Wrong query response type: " << response->toString(); | ||
return; | ||
} | ||
std::forward<Callback>(callback)(*specific_result); | ||
} | ||
|
||
/** | ||
* Check that general command response contains an error with a specific error | ||
* code. | ||
* @param command_result - The response to be checked. | ||
* @param error_code - The expected error code. | ||
*/ | ||
void checkCommandError( | ||
const iroha::ametsuchi::CommandResult &command_result, | ||
iroha::ametsuchi::CommandError::ErrorCodeType error_code); | ||
|
||
/** | ||
* Check that general query response contains a specific error type and | ||
* execute a callback on it. | ||
* @tparam SpecificErrorResponse - Expected specific query error response. | ||
* @param response - The response to be checked. | ||
* @param error_code - The expected error code. | ||
*/ | ||
template <typename SpecificErrorResponse> | ||
void checkQueryError( | ||
const iroha::ametsuchi::QueryExecutorResult &response, | ||
shared_model::interface::ErrorQueryResponse::ErrorCodeType error_code) { | ||
static const auto error_type = typeid(SpecificErrorResponse).name(); | ||
if (auto error = boost::strict_get< | ||
const shared_model::interface::ErrorQueryResponse &>( | ||
&response->get())) { | ||
EXPECT_TRUE( | ||
boost::strict_get<const SpecificErrorResponse &>(&error->get())) | ||
<< "Expected an error of type " << error_type << ", but got " | ||
<< error->toString(); | ||
EXPECT_EQ(error->errorCode(), error_code) | ||
<< "Wrong query result error code!"; | ||
} else { | ||
ADD_FAILURE() << "Expected an error of type " << error_type | ||
<< ", but got " << response->toString(); | ||
} | ||
} | ||
|
||
/// Base class for Executor ITF tests. | ||
class ExecutorTestBase : public ::testing::Test { | ||
public: | ||
void SetUp(); | ||
|
||
iroha::integration_framework::ExecutorItf &getItf() const; | ||
|
||
// ---------------- ledger populators -------------- | ||
|
||
void createAsset( | ||
const std::string &name, | ||
const std::string &domain, | ||
shared_model::interface::types::PrecisionType precision) const; | ||
|
||
void addAsset( | ||
const shared_model::interface::types::AccountIdType &dest_account_id, | ||
const shared_model::interface::types::AssetIdType &asset_id, | ||
const shared_model::interface::Amount &quantity); | ||
|
||
// ---------------- checkers ----------------- | ||
|
||
/// A plain representation of an asset quantity. | ||
struct AssetQuantity { | ||
AssetQuantity(std::string asset_id, const std::string &balance) | ||
: asset_id(std::move(asset_id)), balance(balance) {} | ||
std::string asset_id; | ||
shared_model::interface::Amount balance; | ||
}; | ||
|
||
/** | ||
* Check that the given account assets collection contains the reference | ||
* assets and quantities. | ||
*/ | ||
static void checkAssetQuantities( | ||
const shared_model::interface::types::AccountAssetCollectionType | ||
&test_quantities, | ||
const std::vector<AssetQuantity> &reference_quantities); | ||
|
||
/** | ||
* Check that the given account contains the exact provided assets and | ||
* quantities. | ||
*/ | ||
void checkAssetQuantities(const std::string &account_id, | ||
const std::vector<AssetQuantity> &quantities); | ||
|
||
/// Check that the given account contains the exact provided signatures. | ||
void checkSignatories( | ||
const std::string &account_id, | ||
const std::vector<shared_model::crypto::PublicKey> &keys); | ||
|
||
protected: | ||
virtual std::shared_ptr<ExecutorTestParam> getBackendParam() = 0; | ||
|
||
private: | ||
std::unique_ptr<iroha::integration_framework::ExecutorItf> executor_itf_; | ||
}; | ||
|
||
/** | ||
* A class that provides the backend parameter from GTest parametric test. | ||
* @tparam SpecificQueryFixture Is supposed to be either ExecutorTestBase or | ||
* its derivative. | ||
* | ||
* When different test cases require different parameters, users are supposed | ||
* to implement the required logic in a class derived from ExecutorTestBase, | ||
* and then derive from it helper classes like this to instantiate different | ||
* parametric cases. | ||
*/ | ||
template <typename SpecificQueryFixture> | ||
class BasicExecutorTest : public SpecificQueryFixture, | ||
public ::testing::WithParamInterface< | ||
std::shared_ptr<ExecutorTestParam>> { | ||
protected: | ||
virtual std::shared_ptr<ExecutorTestParam> getBackendParam() { | ||
return GetParam(); | ||
} | ||
}; | ||
|
||
} // namespace executor_testing | ||
|
||
#endif /* TEST_INTEGRATION_EXECUTOR_FIXTURE_HPP */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* Copyright Soramitsu Co., Ltd. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "integration/executor/executor_fixture_param.hpp" | ||
|
||
using namespace executor_testing; | ||
|
||
ExecutorTestParam::~ExecutorTestParam() = default; | ||
|
||
std::string executor_testing::paramToString( | ||
testing::TestParamInfo<std::shared_ptr<ExecutorTestParam>> param) { | ||
return param.param->toString(); | ||
} |
Oops, something went wrong.