From d95e05f3fc6350ae109b95aaa0d1df4cd6ce748f Mon Sep 17 00:00:00 2001 From: alexstroke1 Date: Sat, 2 Nov 2024 20:39:10 +0100 Subject: [PATCH] add three tests to grant permissions Signed-off-by: Far --- pytests/iroha_cli_tests/common/consts.py | 3 + pytests/iroha_cli_tests/common/helpers.py | 2 +- pytests/iroha_cli_tests/src/iroha_cli/have.py | 40 ++++++--- .../src/iroha_cli/iroha_cli.py | 77 ++++++++++------ .../test/permissions/grant_permissions.py | 28 ------ .../permissions/test_grant_permissions.py | 89 +++++++++++++++++++ 6 files changed, 170 insertions(+), 69 deletions(-) delete mode 100644 pytests/iroha_cli_tests/test/permissions/grant_permissions.py create mode 100644 pytests/iroha_cli_tests/test/permissions/test_grant_permissions.py diff --git a/pytests/iroha_cli_tests/common/consts.py b/pytests/iroha_cli_tests/common/consts.py index 837c9eb388e..3cd20b8b5ac 100644 --- a/pytests/iroha_cli_tests/common/consts.py +++ b/pytests/iroha_cli_tests/common/consts.py @@ -19,6 +19,7 @@ class Stderr(Enum): REPETITION = "Repetition" TOO_LONG = "Name length violation" FAILED_TO_FIND_DOMAIN = "Failed to find domain" + FAILED_TO_FIND_ACCOUNT = "Failed to find account" INVALID_CHARACTER = "Failed to parse" INVALID_TYPE = "should be either `Store` or `Numeric`" RESERVED_CHARACTER = ( @@ -27,6 +28,8 @@ class Stderr(Enum): ) WHITESPACES = "White space not allowed" INSUFFICIENT_FUNDS = "Not enough quantity to transfer/burn" + NOT_PERMITTED = "Operation is not permitted: This operation is only allowed inside the genesis block" + UNKNOWN_PERMISSION = "Unknown permission" class ReservedChars(Enum): diff --git a/pytests/iroha_cli_tests/common/helpers.py b/pytests/iroha_cli_tests/common/helpers.py index af144f13e45..39cdcfdf9ab 100644 --- a/pytests/iroha_cli_tests/common/helpers.py +++ b/pytests/iroha_cli_tests/common/helpers.py @@ -17,7 +17,7 @@ def extract_hash(stdout): """ - Extracts a SHA-256 hash from the given string. + Extracts SHA-256 hash from the given string. :param stdout: The string from which to extract the hash. :return: The extracted hash if found, otherwise None. diff --git a/pytests/iroha_cli_tests/src/iroha_cli/have.py b/pytests/iroha_cli_tests/src/iroha_cli/have.py index b27a25419b8..b53c487bb65 100644 --- a/pytests/iroha_cli_tests/src/iroha_cli/have.py +++ b/pytests/iroha_cli_tests/src/iroha_cli/have.py @@ -3,13 +3,15 @@ """ import json +from typing import Any, Callable, Optional import allure # type: ignore from . import iroha_cli, iroha, match +from ...common.helpers import extract_hash -def expected_in_actual(expected, actual) -> bool: +def expected_in_actual(expected: Any, actual: Any) -> bool: """ Check if the expected result is present in the actual result. @@ -18,7 +20,9 @@ def expected_in_actual(expected, actual) -> bool: :return: True if expected is in actual, False otherwise. """ allure.attach( - json.dumps(actual), name="actual", attachment_type=allure.attachment_type.JSON + json.dumps(actual), + name="actual", + attachment_type=allure.attachment_type.JSON, ) allure.attach( json.dumps(expected), @@ -29,12 +33,12 @@ def expected_in_actual(expected, actual) -> bool: return expected in actual -def domain(expected, owned_by=None): +def domain(expected: Any, owned_by: Optional[Any] = None) -> bool: """ Check if the expected domain is present in the list of domains. Optionally checks if the domain is owned by a specific owner. - :param expected: The expected domain object. + :param expected: The expected domain identifier. :param owned_by: The owner of the domain, default is None. :return: True if the domain is present and owned by the specified owner if provided. """ @@ -47,17 +51,16 @@ def domain_in_domains() -> bool: domain_info = domains.get(expected) if not domain_info or domain_info.get("owned_by") != str(owned_by): return False - return True return iroha_cli.wait_for(domain_in_domains) -def account(expected): +def account(expected: Any) -> bool: """ Check if the expected account is present in the list of accounts. - :param expected: The expected account object. + :param expected: The expected account identifier. :return: True if the account is present, False otherwise. """ @@ -68,11 +71,11 @@ def account_in_accounts() -> bool: return iroha_cli.wait_for(account_in_accounts) -def asset_definition(expected): +def asset_definition(expected: Any) -> bool: """ Check if the expected asset definition is present in the list of asset definitions. - :param expected: The expected asset definition object. + :param expected: The expected asset definition identifier. :return: True if the asset definition is present, False otherwise. """ @@ -85,11 +88,11 @@ def asset_definition_in_asset_definitions() -> bool: return iroha_cli.wait_for(asset_definition_in_asset_definitions) -def asset(expected): +def asset(expected: Any) -> bool: """ Check if the expected asset is present in the list of assets. - :param expected: The expected asset object. + :param expected: The expected asset identifier. :return: True if the asset is present, False otherwise. """ @@ -100,7 +103,7 @@ def asset_in_assets() -> bool: return iroha_cli.wait_for(asset_in_assets) -def asset_has_quantity(expected_asset_id, expected_quantity): +def asset_has_quantity(expected_asset_id: Any, expected_quantity: str) -> bool: """ Check if the expected asset quantity is present in the list of assets. @@ -116,7 +119,7 @@ def check_quantity() -> bool: actual_quantity = None for asset_item in assets: if asset_item == expected_asset_id: - actual_quantity = assets.get(expected_asset_id, {})["value"]["Numeric"] + actual_quantity = assets.get(expected_asset_id, {}).get("value", {}).get("Numeric") break if actual_quantity is None: raise ValueError(f"Asset with ID {expected_asset_id} not found.") @@ -137,7 +140,7 @@ def check_quantity() -> bool: return iroha_cli.wait_for(check_quantity) -def error(expected): +def error(expected: Any) -> bool: """ Check if the expected error is present in the iroha_cli stderr. @@ -145,3 +148,12 @@ def error(expected): :return: True if the error is present, False otherwise. """ return match.iroha_cli_have_error(expected=expected, actual=iroha_cli.stderr) + + +def transaction_hash() -> str: + """ + Extract and return the transaction hash from iroha_cli. + + :return: The transaction hash as a string. + """ + return extract_hash(iroha_cli.transaction_hash) diff --git a/pytests/iroha_cli_tests/src/iroha_cli/iroha_cli.py b/pytests/iroha_cli_tests/src/iroha_cli/iroha_cli.py index 35c0ef51473..dabea7fe994 100644 --- a/pytests/iroha_cli_tests/src/iroha_cli/iroha_cli.py +++ b/pytests/iroha_cli_tests/src/iroha_cli/iroha_cli.py @@ -7,7 +7,7 @@ import subprocess from pathlib import Path from time import monotonic, sleep -from typing import Callable +from typing import Callable, Optional import allure # type: ignore @@ -29,11 +29,11 @@ def __init__(self, config: Config): :param config: The configuration object. :type config: Config """ - self.config = config + self._config = config self.command = [self.BASE_PATH] + self.BASE_FLAGS - self.stdout = None - self.stderr = None - self.transaction_hash = None + self.stdout: Optional[str] = None + self.stderr: Optional[str] = None + self.transaction_hash: Optional[str] = None self._timeout = 20 def __enter__(self): @@ -113,6 +113,7 @@ def list_all(self): def list_filter(self, filter_criteria): """ Appends the 'list filter' command to the command list. + :param filter_criteria: Criteria to filter the list. """ self.command.append("list") @@ -181,7 +182,7 @@ def asset(self, asset_definition=None, account=None, value_of_type=None): def transfer(self, asset, source_account, target_account, quantity: str): """ - Executes the 'transfer' command for the given asset + Executes the 'transfer' command for the given asset. :param asset: The asset to be transferred. :type asset: str @@ -213,11 +214,12 @@ def transfer(self, asset, source_account, target_account, quantity: str): def burn(self, account, asset, quantity: str): """ - Executes the 'burn' command for the given asset + Executes the 'burn' command for the given asset. + :param account: The account from which the asset is burned. + :type account: Account :param asset: The asset to be burned. :type asset: str - :param quantity: The quantity of the asset to be burned. :type quantity: str :return: The current IrohaCli object. @@ -282,11 +284,11 @@ def _read_and_update_json(self, template_filename, changes=None): if changes: for key_path, value in changes.items(): - # Update the specific key with the provided value - element = data[0] + # Traverse each path and update the value + element = data[0] # Start from the root element for key in key_path[:-1]: - element = element[key] - element[key_path[-1]] = value + element = element[key] # Traverse through all keys except the last one + element[key_path[-1]] = value # Set the new value at the final key json_temp_file_path = Path(ISI_PATH) / f"isi_{template_filename}" write_isi_to_json(data, str(json_temp_file_path)) @@ -302,7 +304,7 @@ def _execute_isi(self, temp_file_path): """ self._execute_pipe( ["cat", temp_file_path], - [self.BASE_PATH] + self.BASE_FLAGS + ["json"] + ["transaction"], + [self.BASE_PATH] + self.BASE_FLAGS + ["json", "transaction"], ) def register_trigger(self, account): @@ -312,11 +314,10 @@ def register_trigger(self, account): :param account: The account to be used in the register_trigger. :type account: str """ - temp_file_path = self._read_and_update_json( - "register_trigger.json", - ["Register", "Trigger", "action", "authority"], - str(account), - ) + changes = { + ("Register", "Trigger", "action", "authority"): str(account), + } + temp_file_path = self._read_and_update_json("register_trigger.json", changes) self._execute_isi(temp_file_path) return self @@ -327,22 +328,43 @@ def unregister_asset(self, asset): :param asset: The object ID to be used in the unregister_asset. :type asset: str """ - temp_file_path = self._read_and_update_json( - "unregister_asset.json", ["Unregister", "Asset", "object"], str(asset) - ) + changes = { + ("Unregister", "Asset", "object"): str(asset), + } + temp_file_path = self._read_and_update_json("unregister_asset.json", changes) self._execute_isi(temp_file_path) return self def grant_permission(self, destination, permission): + """ + Grants a permission to a destination account. + + :param destination: The account to which the permission is granted. + :type destination: str + :param permission: The permission to grant. + :type permission: str + :return: The current IrohaCli object. + :rtype: IrohaCli + """ changes = { - ("Grant", "Permission", "object"): str(permission), - ("Grant", "Destination", "object"): str(destination), + ("Grant", "Permission", "object", "name"): str(permission), + ("Grant", "Permission", "destination"): str(destination), } temp_file_path = self._read_and_update_json("grant_permission.json", changes) self._execute_isi(temp_file_path) return self def revoke_permission(self, destination, permission): + """ + Revokes a permission from a destination account. + + :param destination: The account from which the permission is revoked. + :type destination: str + :param permission: The permission to revoke. + :type permission: str + :return: The current IrohaCli object. + :rtype: IrohaCli + """ changes = { ("Revoke", "Permission", "object"): str(permission), ("Revoke", "Destination", "object"): str(destination), @@ -353,7 +375,7 @@ def revoke_permission(self, destination, permission): def send_wrong_instruction(self): """ - Creates a JSON file for the send_wrong_instruction executes it using the Iroha CLI. + Creates a JSON file for the send_wrong_instruction and executes it using the Iroha CLI. """ temp_file_path = self._read_and_update_json("wrong_instruction.json") self._execute_isi(temp_file_path) @@ -363,8 +385,8 @@ def should(self, _expected): """ Placeholder method for implementing assertions. - :param expected: The expected value. - :type expected: str + :param _expected: The expected value. + :type _expected: Any :return: The current IrohaCli object. :rtype: IrohaCli """ @@ -374,6 +396,8 @@ def execute(self, command=None): """ Executes the command and captures stdout and stderr. + :param command: The command to execute, defaults to None. + :type command: Optional[List[str]] :return: The current IrohaCli object. :rtype: IrohaCli """ @@ -406,6 +430,7 @@ def _execute_pipe(self, cmd1, cmd2): stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.config.env, + text=True # Ensure that stdout and stderr are strings ) as proc2, ): self.stdout, self.stderr = proc2.communicate() diff --git a/pytests/iroha_cli_tests/test/permissions/grant_permissions.py b/pytests/iroha_cli_tests/test/permissions/grant_permissions.py deleted file mode 100644 index e40dcc8b3f9..00000000000 --- a/pytests/iroha_cli_tests/test/permissions/grant_permissions.py +++ /dev/null @@ -1,28 +0,0 @@ -import allure # type: ignore -import pytest - -from ...src.iroha_cli import iroha_cli, have, iroha - - -@pytest.fixture(scope="function", autouse=True) -def story_account_unregisters_asset(): - allure.dynamic.story("Account grant permission") - -@allure.label("sdk_test_id", "unregister_asset") -@allure.label("permission", "CanRegisterDomain") -def test_grant_permission( - GIVEN_registered_account, - GIVEN_currently_authorized_account -): - with ((allure.step( - f'WHEN "{GIVEN_currently_authorized_account}" grants ' - f'the account "{GIVEN_registered_account}" with CanRegisterDomain permission' - ))): - iroha_cli.grant_permission( - destination=GIVEN_registered_account, - permission='CanRegisterDomain') - - with allure.step( - f'THEN "{GIVEN_registered_account}" can registers domain"' - ): - assert 1 == 1 diff --git a/pytests/iroha_cli_tests/test/permissions/test_grant_permissions.py b/pytests/iroha_cli_tests/test/permissions/test_grant_permissions.py new file mode 100644 index 00000000000..f8ddd71f08c --- /dev/null +++ b/pytests/iroha_cli_tests/test/permissions/test_grant_permissions.py @@ -0,0 +1,89 @@ +import allure # type: ignore +import pytest + +from ...common.consts import Stderr +from ...src.iroha_cli import iroha_cli, have, iroha + + +@pytest.fixture(scope="function", autouse=True) +def story_account_unregisters_asset(): + allure.dynamic.story("Account grant permission") + +@allure.label("sdk_test_id", "grant_permission") +@allure.label("permission", "no_permission_required") +def test_grant_permission( + GIVEN_registered_account, + GIVEN_currently_authorized_account +): + with allure.step( + f'WHEN "{GIVEN_currently_authorized_account}" grants ' + f'the account "{GIVEN_registered_account}" with permission CanSetParameters' + ): + iroha_cli.grant_permission( + destination=GIVEN_registered_account, + permission='CanSetParameters' + ) + + with allure.step( + f'THEN the account "{GIVEN_registered_account}" should have the granted permission' + ): + assert iroha_cli.should(have.transaction_hash()) + +@allure.label("sdk_test_id", "unregister_asset") +@allure.label("permission", "no_permission_required") +def test_grant_not_permitted_permission( + GIVEN_registered_account, + GIVEN_currently_authorized_account +): + with ((allure.step( + f'WHEN "{GIVEN_currently_authorized_account}" grants ' + f'the account "{GIVEN_registered_account}" with not permitted CanRegisterDomain permission' + ))): + iroha_cli.grant_permission( + destination=GIVEN_registered_account, + permission='CanRegisterDomain') + + with allure.step( + f'THEN get an error Operation is not permitted: This operation is only allowed inside the genesis block' + ): + assert iroha_cli.should(have.error(Stderr.NOT_PERMITTED.value)) + +@allure.label("sdk_test_id", "grant_invalid_permission") +@allure.label("permission", "no_permission_required") +def test_grant_invalid_permission( + GIVEN_registered_account, + GIVEN_currently_authorized_account +): + with allure.step( + f'WHEN "{GIVEN_currently_authorized_account}" attempts to grant ' + f'an invalid permission NonExistentPermission to "{GIVEN_registered_account}"' + ): + iroha_cli.grant_permission( + destination=GIVEN_registered_account, + permission='NonExistentPermission' + ) + + with allure.step( + 'THEN get an error stating that the permission is invalid' + ): + assert iroha_cli.should(have.error(Stderr.UNKNOWN_PERMISSION.value)) + +@allure.label("sdk_test_id", "grant_permission_to_nonexistent_account") +@allure.label("permission", "no_permission_required") +def test_grant_permission_to_nonexistent_account( + GIVEN_currently_authorized_account +): + + with allure.step( + f'WHEN "{GIVEN_currently_authorized_account}" attempts to grant ' + f'permission to non existent account' + ): + iroha_cli.grant_permission( + destination='ed01200A303A7FBEDE8FC3D48F46681FF52D533F8B29E564412FA015A68D720C492777@wonderland', + permission='CanSetParameters' + ) + + with allure.step( + 'THEN get an error stating that the destination account does not exist' + ): + assert iroha_cli.should(have.error(Stderr.FAILED_TO_FIND_ACCOUNT.value))