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 3e7dd128367..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. @@ -54,15 +54,16 @@ def read_isi_from_json(file_path): def write_isi_to_json(isi_data, file_path): """ - Writes ISI instruction to a JSON file. + Writes ISI instruction to a JSON file, ensuring the directory exists. :param isi_data: Dictionary with ISI instruction. :param file_path: Path to save the JSON file. """ + os.makedirs(os.path.dirname(file_path), exist_ok=True) if not isinstance(isi_data, list): isi_data = [isi_data] with open(file_path, "w", encoding="utf-8") as file: - json.dump(isi_data, file, indent=4) + file.write(json.dumps(isi_data, indent=4, ensure_ascii=False)) def generate_random_string_with_reserved_char(): diff --git a/pytests/iroha_cli_tests/common/json_isi_examples/grant_permission.json b/pytests/iroha_cli_tests/common/json_isi_examples/grant_permission.json new file mode 100644 index 00000000000..f174165ed1c --- /dev/null +++ b/pytests/iroha_cli_tests/common/json_isi_examples/grant_permission.json @@ -0,0 +1,10 @@ +[ + { + "Grant": { + "Permission": { + "object": {"name": "CanRegisterDomain", "payload": null}, + "destination": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + } + } + } +] diff --git a/pytests/iroha_cli_tests/common/json_isi_examples/revoke_permission.json b/pytests/iroha_cli_tests/common/json_isi_examples/revoke_permission.json new file mode 100644 index 00000000000..960912c7255 --- /dev/null +++ b/pytests/iroha_cli_tests/common/json_isi_examples/revoke_permission.json @@ -0,0 +1,10 @@ +[ + { + "Revoke": { + "Permission": { + "object": {"name": "CanRegisterDomain", "payload": null}, + "destination": "ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + } + } + } +] diff --git a/pytests/iroha_cli_tests/common/json_isi_examples/unregister_asset.json b/pytests/iroha_cli_tests/common/json_isi_examples/unregister_asset.json index 922457f976f..eaa8547c3d9 100644 --- a/pytests/iroha_cli_tests/common/json_isi_examples/unregister_asset.json +++ b/pytests/iroha_cli_tests/common/json_isi_examples/unregister_asset.json @@ -1,7 +1,9 @@ -[{ -"Unregister": { - "Asset": { +[ + { + "Unregister": { + "Asset": { "object": "rose##ed0120CE7FA46C9DCE7EA4B125E2E36BDB63EA33073E7590AC92816AE1E861B7048B03@wonderland" + } } -} -}] + } +] \ No newline at end of file diff --git a/pytests/iroha_cli_tests/src/iroha_cli/have.py b/pytests/iroha_cli_tests/src/iroha_cli/have.py index b27a25419b8..a27db42c0dd 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,9 @@ 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,11 +142,33 @@ def check_quantity() -> bool: return iroha_cli.wait_for(check_quantity) -def error(expected): +def error(expected: str) -> bool: """ Check if the expected error is present in the iroha_cli stderr. :param expected: The expected error message. :return: True if the error is present, False otherwise. """ - return match.iroha_cli_have_error(expected=expected, actual=iroha_cli.stderr) + stderr = iroha_cli.stderr + if stderr is None: + allure.attach( + "stderr is None", + name="actual", + attachment_type=allure.attachment_type.TEXT, + ) + allure.attach( + expected, + name="expected", + attachment_type=allure.attachment_type.TEXT, + ) + return False + return match.iroha_cli_have_error(expected=expected, actual=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 2c6cb5f319d..af1b604e887 100644 --- a/pytests/iroha_cli_tests/src/iroha_cli/iroha_cli.py +++ b/pytests/iroha_cli_tests/src/iroha_cli/iroha_cli.py @@ -7,12 +7,12 @@ import subprocess from pathlib import Path from time import monotonic, sleep -from typing import Callable +from typing import Callable, Optional import allure # type: ignore from ...common.helpers import extract_hash, read_isi_from_json, write_isi_to_json -from ...common.settings import IROHA_CLI_BINARY, ISI_PATH, IROHA_CLI_CONFIG, ROOT_DIR +from ...common.settings import IROHA_CLI_BINARY, ISI_PATH, IROHA_CLI_CONFIG, BASE_DIR from .configuration import Config @@ -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 @@ -215,9 +216,10 @@ def burn(self, account, asset, quantity: str): """ 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. @@ -259,35 +261,26 @@ def asset_definition(self, asset: str, domain: str, type_: str): self.execute() return self - def _read_and_update_json(self, template_filename, key_path=None, value=None): + def _read_and_update_json(self, template_filename, changes=None): """ - Reads a JSON file template, updates a specific key with the provided value, - and writes the updated data to a temporary JSON file. + Reads a JSON template, applies multiple updates, and writes the modified data to a JSON file. :param template_filename: The name of the JSON template file. :type template_filename: str - :param key_path: The path to the key in the JSON data to be updated. - :type key_path: list of str - :param value: The value to set for the specified key. - :type value: str + :param changes: A dictionary of updates where each key is a path to a JSON key + :type changes: dict :return: The path to the temporary JSON file. :rtype: str """ - json_template_path = ( - Path(ROOT_DIR) - / "pytests" - / "common" - / "json_isi_examples" - / template_filename - ) + json_template_path = Path("common") / "json_isi_examples" / template_filename data = read_isi_from_json(str(json_template_path)) - if key_path and value is not None: - # Update the specific key with the provided value - element = data[0] - for key in key_path[:-1]: - element = element[key] - element[key_path[-1]] = value + if changes: + for key_path, value in changes.items(): + element = data[0] + for key in key_path[:-1]: + element = element[key] + element[key_path[-1]] = value json_temp_file_path = Path(ISI_PATH) / f"isi_{template_filename}" write_isi_to_json(data, str(json_temp_file_path)) @@ -303,7 +296,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): @@ -313,11 +306,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 @@ -328,15 +320,54 @@ 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", "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", "name"): str(permission), + ("Revoke", "Permission", "destination"): str(destination), + } + temp_file_path = self._read_and_update_json("revoke_permission.json", changes) self._execute_isi(temp_file_path) return self 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) @@ -346,8 +377,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 """ @@ -357,6 +388,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 """ @@ -389,6 +422,7 @@ def _execute_pipe(self, cmd1, cmd2): stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.config.env, + text=True, ) as proc2, ): self.stdout, self.stderr = proc2.communicate() diff --git a/pytests/iroha_cli_tests/test/conftest.py b/pytests/iroha_cli_tests/test/conftest.py index ae11957dcf6..e9754c1658c 100644 --- a/pytests/iroha_cli_tests/test/conftest.py +++ b/pytests/iroha_cli_tests/test/conftest.py @@ -6,6 +6,8 @@ import allure # type: ignore import pytest +from typing import Any, Generator, List + from ..common.consts import ValueTypes from ..common.helpers import ( fake_asset_name, @@ -27,7 +29,7 @@ # General fixtures @pytest.fixture(scope="session", autouse=True) -def before_all(): +def before_all() -> Generator[None, None, None]: """Initial setup for all test sessions. This fixture generates configurations based on peers and is automatically used for every test session.""" @@ -36,7 +38,7 @@ def before_all(): @pytest.fixture(scope="function", autouse=True) -def before_each(): +def before_each() -> Generator[None, None, None]: """Fixture to set up and reset the iroha_cli state.""" allure.dynamic.label("sdk", "Client CLI") allure.dynamic.label("owner", "astrokov") @@ -46,7 +48,7 @@ def before_each(): # Fixtures for creating objects (domains, accounts, asset definitions, assets) @pytest.fixture() -def GIVEN_registered_domain(): +def GIVEN_registered_domain() -> Domain: """Fixture to create and register a domain.""" domain = Domain(fake_name()) with allure.step(f"GIVEN a registered domain {domain.name}"): @@ -55,8 +57,10 @@ def GIVEN_registered_domain(): @pytest.fixture() -def GIVEN_registered_domain_with_uppercase_letter(GIVEN_registered_domain): - """Fixture to create and register a domain, but with uppercase letter.""" +def GIVEN_registered_domain_with_uppercase_letter( + GIVEN_registered_domain: Domain, +) -> Domain: + """Fixture to create and register a domain, but with an uppercase letter.""" domain = GIVEN_registered_domain domain.name = name_with_uppercase_letter(domain.name) with allure.step(f"GIVEN a registered domain {domain.name}"): @@ -65,20 +69,36 @@ def GIVEN_registered_domain_with_uppercase_letter(GIVEN_registered_domain): @pytest.fixture() -def GIVEN_registered_account(GIVEN_registered_domain, GIVEN_public_key): - """Fixture to create an account.""" +def GIVEN_registered_account( + GIVEN_registered_domain: Domain, GIVEN_public_key: str +) -> Account: + """Fixture to create and register an account.""" account = Account(signatory=GIVEN_public_key, domain=GIVEN_registered_domain.name) with allure.step( - f'GIVEN the account "{GIVEN_public_key}" in the "{GIVEN_registered_domain.name}" domain' + f'GIVEN the account "{account.signatory}" in the "{account.domain}" domain' ): iroha_cli.register().account(signatory=account.signatory, domain=account.domain) return account @pytest.fixture() -def GIVEN_currently_authorized_account(): +def GIVEN_registered_account_granted_with_CanSetParameters( + GIVEN_registered_account: Account, GIVEN_currently_authorized_account: Account +) -> Account: + """Fixture to grant the account with CanSetParameters permission.""" + with allure.step( + f'GIVEN "{GIVEN_registered_account}" granted with permission CanSetParameters' + ): + iroha_cli.grant_permission( + destination=GIVEN_registered_account, permission="CanSetParameters" + ) + return GIVEN_registered_account + + +@pytest.fixture() +def GIVEN_currently_authorized_account() -> Account: """Fixture to get the currently authorized account.""" - account: Account = Account( + account = Account( signatory=config.account_signatory, domain=config.account_domain, ) @@ -86,25 +106,30 @@ def GIVEN_currently_authorized_account(): f'GIVEN the currently authorized account "{account.signatory}" ' f'in the "{account.domain}" domain' ): - return account + pass + return account @pytest.fixture() def GIVEN_currently_account_quantity_with_two_quantity_of_asset( - GIVEN_currently_authorized_account, GIVEN_numeric_type, GIVEN_fake_asset_name -): - """Fixture to get the currently authorized account asset""" + GIVEN_currently_authorized_account: Account, + GIVEN_numeric_type: str, + GIVEN_fake_asset_name: str, +) -> Asset: + """Fixture to get the currently authorized account's asset with a quantity of 2.""" asset_def = AssetDefinition( name=GIVEN_fake_asset_name, domain=GIVEN_currently_authorized_account.domain, type_=GIVEN_numeric_type, ) asset = Asset( - definition=asset_def, value="2", account=GIVEN_currently_authorized_account + definition=asset_def, + value="2", + account=GIVEN_currently_authorized_account, ) name = fake_name() with allure.step( - f'GIVEN the asset_definition "{name}" ' + f'GIVEN the asset definition "{name}" ' f'in the "{GIVEN_currently_authorized_account.domain}" domain' ): iroha_cli.register().asset_definition( @@ -122,11 +147,14 @@ def GIVEN_currently_account_quantity_with_two_quantity_of_asset( @pytest.fixture() def GIVEN_numeric_asset_for_account( - request, GIVEN_numeric_type, GIVEN_fake_asset_name, GIVEN_numeric_value -): - """Fixture to get an asset for a given account and domain with specified quantity.""" - account, domain = request.param.split("@") - account = Account(signatory=account, domain=domain) + request: Any, + GIVEN_numeric_type: str, + GIVEN_fake_asset_name: str, + GIVEN_numeric_value: str, +) -> Asset: + """Fixture to get an asset for a given account and domain with a specified quantity.""" + account_str, domain = request.param.split("@") + account = Account(signatory=account_str, domain=domain) asset_def = AssetDefinition( name=GIVEN_fake_asset_name, domain=domain, type_=GIVEN_numeric_type @@ -136,7 +164,7 @@ def GIVEN_numeric_asset_for_account( ) with allure.step( - f'GIVEN the asset_definition "{asset_def.name}" ' f'in the "{domain}" domain' + f'GIVEN the asset definition "{asset_def.name}" in the "{domain}" domain' ): iroha_cli.register().asset_definition( asset=asset.definition.name, @@ -154,17 +182,19 @@ def GIVEN_numeric_asset_for_account( @pytest.fixture() def GIVEN_registered_asset_definition_with_numeric_type( - GIVEN_registered_domain, GIVEN_numeric_type, GIVEN_fake_asset_name -): - """Fixture to create and register an asset definition with numeric value type.""" + GIVEN_registered_domain: Domain, + GIVEN_numeric_type: str, + GIVEN_fake_asset_name: str, +) -> AssetDefinition: + """Fixture to create and register an asset definition with a numeric value type.""" asset_def = AssetDefinition( name=GIVEN_fake_asset_name, domain=GIVEN_registered_domain.name, type_=GIVEN_numeric_type, ) with allure.step( - f'GIVEN the asset_definition "{GIVEN_fake_asset_name}" ' - f'in the "{GIVEN_registered_domain.name}" domain' + f'GIVEN the asset definition "{asset_def.name}" ' + f'in the "{asset_def.domain}" domain' ): iroha_cli.register().asset_definition( asset=asset_def.name, @@ -176,13 +206,11 @@ def GIVEN_registered_asset_definition_with_numeric_type( @pytest.fixture() def GIVEN_minted_asset_quantity( - GIVEN_registered_asset_definition_with_numeric_type, - GIVEN_registered_account, - GIVEN_numeric_value, -): - """Fixture to create and return an asset with a specified quantity. - It takes a registered asset definition, a registered account, and a numeric value. - """ + GIVEN_registered_asset_definition_with_numeric_type: AssetDefinition, + GIVEN_registered_account: Account, + GIVEN_numeric_value: str, +) -> Asset: + """Fixture to create and return an asset with a specified quantity.""" asset = Asset( account=GIVEN_registered_account, definition=GIVEN_registered_asset_definition_with_numeric_type, @@ -198,143 +226,156 @@ def GIVEN_minted_asset_quantity( @pytest.fixture() def GIVEN_registered_asset_definition_with_store_type( - GIVEN_registered_domain, GIVEN_store_type, GIVEN_fake_asset_name -): - """Fixture to create and register an asset definition with store value type.""" + GIVEN_registered_domain: Domain, GIVEN_store_type: str, GIVEN_fake_asset_name: str +) -> AssetDefinition: + """Fixture to create and register an asset definition with a store value type.""" asset_def = AssetDefinition( name=GIVEN_fake_asset_name, domain=GIVEN_registered_domain.name, type_=GIVEN_store_type, ) with allure.step( - f'GIVEN the asset_definition "{GIVEN_fake_asset_name}" ' - f'in the "{GIVEN_registered_domain.name}" domain' + f'GIVEN the asset definition "{asset_def.name}" ' + f'in the "{asset_def.domain}" domain' ): iroha_cli.register().asset_definition( asset=asset_def.name, domain=asset_def.domain, - type=asset_def.type, + type_=asset_def.type_, ) return asset_def # Fixtures for generating various types of data (strings, keys, names, etc.) @pytest.fixture() -def GIVEN_fake_name(): +def GIVEN_fake_name() -> str: """Fixture to provide a fake name.""" name = fake_name() with allure.step(f'GIVEN a "{name}" name'): - return name + pass + return name @pytest.fixture() -def GIVEN_fake_asset_name(): +def GIVEN_fake_asset_name() -> str: """Fixture to provide a fake asset name.""" asset_name = fake_asset_name() with allure.step(f'GIVEN a "{asset_name}" asset'): - return asset_name + pass + return asset_name @pytest.fixture() -def GIVEN_not_existing_name(): +def GIVEN_not_existing_name() -> str: """Fixture to provide a non-existing name.""" name = not_existing_name() - with allure.step(f"GIVEN an non-existing {name}"): - return name + with allure.step(f"GIVEN a non-existing {name}"): + pass + return name @pytest.fixture() -def GIVEN_public_key(): +def GIVEN_public_key() -> str: """Fixture to provide a public key.""" public_key = generate_public_key() with allure.step(f"GIVEN a public key {public_key}"): - return public_key + pass + return public_key @pytest.fixture() -def GIVEN_random_character(): - """Fixture to provide a random character from the ASCII letters.""" +def GIVEN_random_character() -> str: + """Fixture to provide a random character from ASCII letters.""" letter = random.choice(string.ascii_letters) - with allure.step(f'GIVEN a "{letter}" name'): - return letter + with allure.step(f'GIVEN a "{letter}" character'): + pass + return letter @pytest.fixture() -def GIVEN_random_invalid_base64_character(): - """Fixture to provide a random invalid base64 character - (not a-z,A-Z,0-9,+,/,=). - """ +def GIVEN_random_invalid_base64_character() -> str: + """Fixture to provide a random invalid base64 character.""" invalid_chars = [ ch for ch in string.printable if not (ch.isalpha() or ch.isdigit() or ch in ["=", "+", "/"]) ] letter = random.choice(invalid_chars) - with allure.step(f'GIVEN a "{letter}" name'): - return letter + with allure.step(f'GIVEN an invalid base64 character "{letter}"'): + pass + return letter -# Fixtures for providing specific values or conditions (e.g., name length, string with spaces) +# Fixtures for providing specific values or conditions (e.g., name length, strings with spaces) @pytest.fixture() def GIVEN_key_with_invalid_character_in_key( - GIVEN_public_key, GIVEN_random_invalid_base64_character -): + GIVEN_public_key: str, GIVEN_random_invalid_base64_character: str +) -> str: """Fixture to provide a public key with an invalid character.""" invalid_key = key_with_invalid_character_in_key( GIVEN_public_key, GIVEN_random_invalid_base64_character ) with allure.step(f'GIVEN an invalid key "{invalid_key}"'): - return invalid_key + pass + return invalid_key @pytest.fixture() -def GIVEN_numeric_type(): +def GIVEN_numeric_type() -> str: """Fixture to provide a numeric value type.""" type_ = ValueTypes.NUMERIC.value with allure.step(f'GIVEN a "{type_}" value type'): - return type_ + pass + return type_ @pytest.fixture() -def GIVEN_store_type(): +def GIVEN_store_type() -> str: """Fixture to provide a store value type.""" type_ = ValueTypes.STORE.value with allure.step(f'GIVEN a "{type_}" value type'): - return type_ + pass + return type_ @pytest.fixture() -def GIVEN_numeric_value(): - """Fixture to provide a random numeric value based on the given value type.""" +def GIVEN_numeric_value() -> str: + """Fixture to provide a random numeric value.""" rand_int = str((random.getrandbits(96)) - 1) return rand_int @pytest.fixture() -def GIVEN_128_length_name(): +def GIVEN_128_length_name() -> str: + """Fixture to provide a string with 128 characters.""" ident = generate_random_string_without_reserved_chars(128) - with allure.step(f'GIVEN a name with 128 length "{ident}"'): - return ident + with allure.step(f'GIVEN a name with 128 characters "{ident}"'): + pass + return ident @pytest.fixture() -def GIVEN_129_length_name(): +def GIVEN_129_length_name() -> str: + """Fixture to provide a string with 129 characters.""" ident = generate_random_string_without_reserved_chars(129) - with allure.step(f'GIVEN a name with 129 length "{ident}"'): - return ident + with allure.step(f'GIVEN a name with 129 characters "{ident}"'): + pass + return ident @pytest.fixture() -def GIVEN_string_with_reserved_character(): - """Fixture to provide a random string with reserved characters.""" +def GIVEN_string_with_reserved_character() -> str: + """Fixture to provide a string with reserved characters.""" new_string = generate_random_string_with_reserved_char() - with allure.step(f'GIVEN a "{new_string}" string'): - return new_string + with allure.step(f'GIVEN a string with reserved characters "{new_string}"'): + pass + return new_string @pytest.fixture() -def GIVEN_string_with_whitespaces(): - """Fixture to provide a random string with whitespaces.""" +def GIVEN_string_with_whitespaces() -> str: + """Fixture to provide a string with whitespaces.""" new_string = generate_random_string_with_whitespace() - with allure.step(f'GIVEN a "{new_string}" string'): - return new_string + with allure.step(f'GIVEN a string with whitespaces "{new_string}"'): + pass + return new_string diff --git a/pytests/iroha_cli_tests/test/permissions/__init__.py b/pytests/iroha_cli_tests/test/permissions/__init__.py new file mode 100644 index 00000000000..69797f0db8a --- /dev/null +++ b/pytests/iroha_cli_tests/test/permissions/__init__.py @@ -0,0 +1,14 @@ +from .. import ( + GIVEN_currently_authorized_account, + GIVEN_registered_account, + before_all, + before_each, +) + +import allure # type: ignore +import pytest + + +@pytest.fixture(scope="function", autouse=True) +def domain_test_setup(): + allure.dynamic.feature("Permissions") 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..93e8ecf15e6 --- /dev/null +++ b/pytests/iroha_cli_tests/test/permissions/test_grant_permissions.py @@ -0,0 +1,83 @@ +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", "grant_not_permitted_permission") +@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( + "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)) diff --git a/pytests/iroha_cli_tests/test/permissions/test_revoke_permissions.py b/pytests/iroha_cli_tests/test/permissions/test_revoke_permissions.py new file mode 100644 index 00000000000..ce13d29a091 --- /dev/null +++ b/pytests/iroha_cli_tests/test/permissions/test_revoke_permissions.py @@ -0,0 +1,128 @@ +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 revoke permission") + + +@allure.label("sdk_test_id", "revoke_permission") +@allure.label("permission", "no_permission_required") +@pytest.mark.xfail(reason="wait for #3036") +def test_revoke_permission( + GIVEN_registered_account_granted_with_CanSetParameters, + GIVEN_currently_authorized_account, +): + with allure.step( + f'WHEN "{GIVEN_currently_authorized_account}" revokes CanSetParameters' + f'from the "{GIVEN_registered_account_granted_with_CanSetParameters}"' + ): + iroha_cli.revoke_permission( + destination=GIVEN_registered_account_granted_with_CanSetParameters, + permission="CanSetParameters", + ) + + with allure.step( + f'THEN the account "{GIVEN_registered_account_granted_with_CanSetParameters}" should be revoked' + ): + assert iroha_cli.should(have.transaction_hash()) + + +@allure.label("sdk_test_id", "revoke_permission_not_granted") +@allure.label("permission", "no_permission_required") +@pytest.mark.xfail(reason="wait for #3036") +def test_revoke_not_granted_permission( + GIVEN_registered_account, GIVEN_currently_authorized_account +): + with allure.step( + f'WHEN "{GIVEN_currently_authorized_account}" tries to revoke permission ' + f'CanSetParameters from "{GIVEN_registered_account}" which was not granted' + ): + iroha_cli.revoke_permission( + destination=GIVEN_registered_account, permission="CanSetParameters" + ) + + with allure.step("THEN the system should handle the operation appropriately"): + assert iroha_cli.should(have.transaction_hash()) + + +@allure.label("sdk_test_id", "revoke_permission_nonexistent_account") +@allure.label("permission", "no_permission_required") +@pytest.mark.xfail(reason="wait for #3036") +def test_revoke_permission_nonexistent_account(GIVEN_currently_authorized_account): + with allure.step( + f'WHEN "{GIVEN_currently_authorized_account}" tries to revoke permission ' + f"CanSetParameters from non-existent account nonexistent@domain" + ): + iroha_cli.revoke_permission( + destination="nonexistent@domain", permission="CanSetParameters" + ) + + with allure.step( + "THEN the system should return an error indicating the account does not exist" + ): + assert iroha_cli.should(have.error(Stderr.ACCOUNT_NOT_FOUND.value)) + + +@allure.label("sdk_test_id", "revoke_permission_without_rights") +@allure.label("permission", "no_permission_required") +@pytest.mark.xfail(reason="wait for #3036") +def test_revoke_permission_without_rights( + GIVEN_registered_account_granted_with_CanSetParameters, + GIVEN_account_without_revoke_rights, +): + with allure.step( + f'WHEN "{GIVEN_account_without_revoke_rights}" tries to revoke permission ' + f'CanSetParameters from "{GIVEN_registered_account_granted_with_CanSetParameters}"' + ): + iroha_cli.revoke_permission( + destination=GIVEN_registered_account_granted_with_CanSetParameters, + permission="CanSetParameters", + ) + + with allure.step( + "THEN the system should return an error indicating insufficient permissions" + ): + assert iroha_cli.should(have.error(Stderr.NOT_PERMITTED.value)) + + +@allure.label("sdk_test_id", "revoke_permission_from_self") +@allure.label("permission", "no_permission_required") +@pytest.mark.xfail(reason="wait for #3036") +def test_revoke_permission_from_self( + GIVEN_currently_authorized_account_granted_with_CanSetParameters, +): + with allure.step( + f'WHEN "{GIVEN_currently_authorized_account_granted_with_CanSetParameters}" tries to revoke ' + f"permission CanSetParameters from itself" + ): + iroha_cli.revoke_permission( + destination=GIVEN_currently_authorized_account_granted_with_CanSetParameters, + permission="CanSetParameters", + ) + + with allure.step( + "THEN the operation should be processed according to the system logic" + ): + assert iroha_cli.should(have.transaction_hash()) + + +@allure.label("sdk_test_id", "revoke_permission_invalid_data_format") +@allure.label("permission", "no_permission_required") +@pytest.mark.xfail(reason="wait for #3036") +def test_revoke_permission_invalid_data_format(GIVEN_registered_account): + with allure.step( + f'WHEN attempting to revoke permission with invalid data format from "{GIVEN_registered_account}"' + ): + iroha_cli.revoke_permission( + destination=GIVEN_registered_account, permission=12345 + ) + + with allure.step( + "THEN the system should return an error due to invalid data format" + ): + assert iroha_cli.should(have.error(Stderr.INVALID_INPUT.value))