Skip to content
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

[refactor] #3719: Migrate integration Rust tests to client cli pytests #4036

Merged
merged 5 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/iroha2-dev-pr-static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- '**.json'
- '**.toml'
- '.github/workflows/**.yml'
- 'client_cli/pytests/**/*.py'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -54,3 +55,31 @@ jobs:
- name: Documentation
if: always()
run: cargo doc --no-deps --quiet
python_static_analysis:
runs-on: ubuntu-latest
container:
image: hyperledger/iroha2-ci:nightly-2024-01-12
steps:
- uses: actions/checkout@v4

- name: Install dependencies using Poetry
working-directory: client_cli/pytests
run: |
poetry install

- name: Check code formatting with Black
working-directory: client_cli/pytests
run: |
poetry run black --check .

- name: Run mypy (Type Checker)
working-directory: client_cli/pytests
run: |
poetry run mypy --explicit-package-bases .

- name: Run flake8 (Linter)
working-directory: client_cli/pytests
run: |
poetry run flake8 . --max-line-length=110 --ignore=F401,W503,E203


4 changes: 2 additions & 2 deletions .github/workflows/iroha2-dev-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ jobs:
- name: Run tests, with coverage
run: |
mold --run cargo llvm-cov clean --workspace
mold --run cargo llvm-cov --doc --no-report --all-features --workspace --no-fail-fast
mold --run cargo llvm-cov --no-report --ignore-filename-regex main.rs --all-features --workspace --no-fail-fast
mold --run cargo llvm-cov --doc --no-report --all-features --workspace --no-fail-fast --ignore-filename-regex='(^client_cli/|main\.rs)'
mold --run cargo llvm-cov --no-report --ignore-filename-regex='(^client_cli/|main\.rs)' --all-features --workspace --no-fail-fast
- name: Generate lcov report
run: |
# generate report without tests
Expand Down
2 changes: 2 additions & 0 deletions client/tests/integration/add_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use iroha_config::parameters::actual::Root as Config;
use test_network::*;

#[test]
// This test suite is also covered at the UI level in the iroha_client_cli tests
// in test_register_accounts.py
fn client_add_account_with_name_length_more_than_limit_should_not_commit_transaction() -> Result<()>
{
let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(10_505).start_with_runtime();
Expand Down
2 changes: 2 additions & 0 deletions client/tests/integration/add_domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use iroha_config::parameters::actual::Root as Config;
use test_network::*;

#[test]
// This test suite is also covered at the UI level in the iroha_client_cli tests
// in test_register_domains.py
fn client_add_domain_with_name_length_more_than_limit_should_not_commit_transaction() -> Result<()>
{
let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(10_500).start_with_runtime();
Expand Down
6 changes: 6 additions & 0 deletions client/tests/integration/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use serde_json::json;
use test_network::*;

#[test]
// This test is also covered at the UI level in the iroha_client_cli tests
// in test_register_asset_definitions.py
fn client_register_asset_should_add_asset_once_but_not_twice() -> Result<()> {
let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(10_620).start_with_runtime();
wait_for_genesis_committed(&[test_client.clone()], 0);
Expand Down Expand Up @@ -89,6 +91,8 @@ fn unregister_asset_should_remove_asset_from_account() -> Result<()> {
}

#[test]
// This test is also covered at the UI level in the iroha_client_cli tests
// in test_mint_assets.py
fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount() -> Result<()> {
let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(10_000).start_with_runtime();
wait_for_genesis_committed(&[test_client.clone()], 0);
Expand Down Expand Up @@ -202,6 +206,8 @@ fn client_add_asset_with_decimal_should_increase_asset_amount() -> Result<()> {
}

#[test]
// This test is also covered at the UI level in the iroha_client_cli tests
// in test_register_asset_definitions.py
fn client_add_asset_with_name_length_more_than_limit_should_not_commit_transaction() -> Result<()> {
let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(10_520).start_with_runtime();
wait_for_genesis_committed(&[test_client.clone()], 0);
Expand Down
2 changes: 2 additions & 0 deletions client/tests/integration/asset_propagation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use iroha_config::parameters::actual::Root as Config;
use test_network::*;

#[test]
// This test is also covered at the UI level in the iroha_client_cli tests
// in test_mint_asset.py
fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount_on_another_peer(
) -> Result<()> {
// Given
Expand Down
41 changes: 0 additions & 41 deletions client/tests/integration/config.rs

This file was deleted.

6 changes: 6 additions & 0 deletions client/tests/integration/extra_functional/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mod connected_peers;
mod multiple_blocks_created;
mod offline_peers;
mod restart_peer;
mod unregister_peer;
mod unstable_network;
11 changes: 2 additions & 9 deletions client/tests/integration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,20 @@ mod add_domain;
mod asset;
mod asset_propagation;
mod burn_public_keys;
mod config;
mod connected_peers;
mod domain_owner;
mod domain_owner_permissions;
mod events;
mod multiple_blocks_created;
mod extra_functional;
mod multisignature_account;
mod multisignature_transaction;
mod non_mintable;
mod offline_peers;
mod pagination;
mod permissions;
mod queries;
mod query_errors;
mod restart_peer;
mod roles;
mod set_parameter;
mod sorting;
mod transfer_asset;
mod triggers;
mod tx_history;
mod tx_rollback;
mod unregister_peer;
mod unstable_network;
mod upgrade;
1 change: 1 addition & 0 deletions client/tests/integration/queries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use test_network::*;

mod account;
mod asset;
mod query_errors;
mod role;

#[test]
Expand Down
4 changes: 4 additions & 0 deletions client/tests/integration/transfer_asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use iroha_primitives::fixed::Fixed;
use test_network::*;

#[test]
// This test suite is also covered at the UI level in the iroha_client_cli tests
// in test_tranfer_assets.py
fn simulate_transfer_quantity() {
simulate_transfer(
200_u32,
Expand Down Expand Up @@ -53,6 +55,8 @@ fn simulate_transfer_fixed() {
#[test]
#[ignore = "long"]
#[should_panic(expected = "insufficient funds")]
// This test is also covered at the UI level in the iroha_client_cli tests
// in test_tranfer_assets.py
fn simulate_insufficient_funds() {
simulate_transfer(
Fixed::try_from(20_f64).unwrap(),
Expand Down
Empty file.
28 changes: 17 additions & 11 deletions client_cli/pytests/common/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,26 @@ class Stderr(Enum):
"""
Enum for standard error messages.
"""
CANNOT_BE_EMPTY = 'cannot be empty\n\nFor more information, try \'--help\'.\n'
REPETITION = 'Repetition'
TOO_LONG = 'Name length violation'
FAILED_TO_FIND_DOMAIN = 'Failed to find domain'
INVALID_CHARACTER = 'Invalid character'
INVALID_VALUE_TYPE = 'Matching variant not found'
RESERVED_CHARACTER = 'The `@` character is reserved for `account@domain` constructs,' \
' `#` — for `asset#domain` and `$` — for `trigger$domain`.'

CANNOT_BE_EMPTY = "cannot be empty\n\nFor more information, try '--help'.\n"
REPETITION = "Repetition"
TOO_LONG = "Name length violation"
FAILED_TO_FIND_DOMAIN = "Failed to find domain"
INVALID_CHARACTER = "Invalid character"
INVALID_VALUE_TYPE = "Matching variant not found"
RESERVED_CHARACTER = (
"The `@` character is reserved for `account@domain` constructs,"
" `#` — for `asset#domain` and `$` — for `trigger$domain`."
)
WHITESPACES = "White space not allowed"
INSUFFICIENT_FUNDS = "Not enough quantity to transfer/burn"


class ReservedChars(Enum):
"""
Enum for reserved characters in names.
"""

SPECIAL = "@#$"
WHITESPACES = string.whitespace
ALL = SPECIAL + WHITESPACES
Expand All @@ -38,8 +43,9 @@ class ValueTypes(Enum):
"""
Enum for value types used in the application.
"""
QUANTITY = 'Quantity' # unsigned 32-bit integer
STORE = 'Store' #storing key-values in object's metadata
# BIG_QUANTITY = 'BigQuantity' unsigned 128-bit integer

QUANTITY = "Quantity" # unsigned 32-bit integer
STORE = "Store" # storing key-values in object's metadata
BIG_QUANTITY = "BigQuantity" # unsigned 128-bit integer
# FIXED = 'Fixed' 64-bit fixed-precision number with
# nine significant digits after the decimal point
79 changes: 72 additions & 7 deletions client_cli/pytests/common/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,68 @@
"""

import binascii
import json
import os
import random
import re
import string

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

from common.consts import ReservedChars, fake


def extract_hash(stdout):
"""
Extracts a 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.
"""
if not isinstance(stdout, str) or not stdout.strip():
return None
pattern = r'"([A-Fa-f0-9]{64})"'
match = re.search(pattern, stdout)
return match.group(1) if match else None


def get_peers_config_files(path_to_configs):
"""
Returns a list of config file paths from the given directory.
"""
config_files = []
for entry in os.listdir(path_to_configs):
if entry.endswith(".json") and "config_to_peer" in entry:
config_files.append(os.path.join(path_to_configs, entry))
return config_files


def read_isi_from_json(file_path):
"""
Reads ISI instruction from a JSON file.

:param file_path: Path to the JSON file containing ISI instruction.
:return: Dictionary with ISI instruction.
"""
with open(file_path, "r", encoding="utf-8") as file:
isi_data = json.load(file)
return isi_data


def write_isi_to_json(isi_data, file_path):
"""
Writes ISI instruction to a JSON file.

:param isi_data: Dictionary with ISI instruction.
:param file_path: Path to save the JSON file.
"""
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)


def generate_random_string_with_reserved_char():
"""
Generate a random string with a reserved character.
Expand Down Expand Up @@ -38,24 +92,31 @@ def generate_public_key():
Generate a public key using Ed25519PrivateKey.
"""
public_key = binascii.hexlify(
Ed25519PrivateKey.generate().public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw)).decode()
Ed25519PrivateKey.generate()
.public_key()
.public_bytes(
encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw
)
).decode()
return public_key


def generate_random_string(length, allowed_chars):
"""
Generate a random string with the specified length and characters.
"""
return ''.join(random.choice(allowed_chars) for _ in range(length))
return "".join(random.choice(allowed_chars) for _ in range(length))


def generate_random_string_without_reserved_chars(length):
"""
Generate a random string with the specified length, excluding reserved characters.
"""
allowed_chars = [c for c in [*string.ascii_letters, *string.digits] if c not in ReservedChars.ALL.value]
allowed_chars = [
c
for c in [*string.ascii_letters, *string.digits]
if c not in ReservedChars.ALL.value
]
return generate_random_string(length, allowed_chars)


Expand All @@ -78,7 +139,7 @@ def not_existing_name():
"""
Generate a non-existing name.
"""
return 'not_existing_name'
return "not_existing_name"


def key_with_invalid_character_in_key(public_key, random_character):
Expand All @@ -93,5 +154,9 @@ def name_with_uppercase_letter(name):
Change one random letter in a name to uppercase.
"""
random_position = random.randint(0, len(name) - 1)
name = name[:random_position] + name[random_position].upper() + name[random_position + 1:]
name = (
name[:random_position]
+ name[random_position].upper()
+ name[random_position + 1 :]
)
return name
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[{
"Unregister": {
"Asset": {
"object_id": "rose#alice@wonderland"
}
}
}]
Loading
Loading