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

Added cli validators test in pytest and CI for localnet dependent tests. #454

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c32d328
Added cli validators test in pytest and CI for localnet dependent tests.
cristure Oct 24, 2024
84efeb2
Added fixture for proxy polling.
cristure Oct 24, 2024
1685f02
try different endpoint for polling.
cristure Oct 24, 2024
f4e9c73
adding debug command.
cristure Oct 24, 2024
315e3b0
more debugging.
cristure Oct 24, 2024
39be1b4
make localnet run in background.
cristure Oct 24, 2024
18d6c71
added Dockerfile and integrated CI with docker.
cristure Oct 24, 2024
3f825a2
add checkout.
cristure Oct 24, 2024
99a198d
install python dependencies in order to trigger the tests.
cristure Oct 24, 2024
3d2be36
rename to localhost. possible issue with the loopback interface.
cristure Oct 24, 2024
beef139
debug commands
cristure Oct 24, 2024
95f1a15
more debugging.
cristure Oct 24, 2024
23b9961
add a sleep for debugging.
cristure Oct 24, 2024
ef9cdb2
trying to debug docker publishing in GHA.
cristure Oct 24, 2024
5043947
cosmetic changes
cristure Oct 24, 2024
4953eaa
move sleep above.
cristure Oct 24, 2024
a5ccb03
more debugging.
cristure Oct 24, 2024
7e889f1
increase timeout to 10 mins.
cristure Oct 24, 2024
0af4d95
check logs of docker localnet inside worker.
cristure Oct 24, 2024
05a7e57
fix CI and dockerfile by installing build-essential in the docker con…
cristure Oct 24, 2024
ec17dda
adding verbose command.
cristure Oct 24, 2024
1a4fe12
Fix deploy localnet job.
cristure Oct 25, 2024
900530f
fix whitespaces in multi-line command.
cristure Oct 25, 2024
17a08cf
fixing docker image and localnet setup.
cristure Oct 25, 2024
8359cbb
adding separate validators json file for docker tests.
cristure Oct 25, 2024
b43c31a
fixing Dockerfile
cristure Oct 25, 2024
bbe93e7
fix paths for validators_docker.json
cristure Oct 25, 2024
ad7e421
fix paths in validators_docker.json
cristure Oct 25, 2024
bfe909f
fix validators_docker.json
cristure Oct 25, 2024
83b76aa
fix yaml file.
cristure Oct 25, 2024
8804754
fix yaml file, round 2.
cristure Oct 25, 2024
d3d0d5c
add debug commands.
cristure Oct 25, 2024
9f9b8c0
debug home directory inside runner.
cristure Oct 25, 2024
79392ad
fix paths.
cristure Oct 25, 2024
d3f707d
more debugging.
cristure Oct 25, 2024
adaf45a
fix more paths.
cristure Oct 25, 2024
28fa958
add debug sleep.
cristure Oct 25, 2024
0e46464
cosmetic changes.
cristure Oct 25, 2024
7045c32
fix yaml file.
cristure Oct 25, 2024
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
54 changes: 54 additions & 0 deletions .github/workflows/test-localnet-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Test localnet-dependent tests

on:
pull_request:
branches: [main, feat/*]
workflow_dispatch:

env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}

jobs:
localnet:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: [3.11]

steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
pip3 install -r requirements.txt
pip3 install -r ./requirements-dev.txt --upgrade

- name: Set up Docker image
run: |
docker build --build-arg PYTHON_VERSION=${{ matrix.python-version }} -t mxpy .

- name: Deploy a localnet
run: |
docker run --name localnet -d -p 7950:7950 mxpy localnet start \
--configfile=/usr/src/app/multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml

# TODO: introduce polling in test fixture
# remove sleep
- name: Test localnet dependent tests
run: |
docker cp localnet:/usr/src/app/localnet/validator00/config/validatorKey.pem ./validatorKey00.pem
docker cp localnet:/usr/src/app/localnet/validator01/config/validatorKey.pem ./validatorKey01.pem
docker cp localnet:/usr/src/app/localnet/validator02/config/validatorKey.pem ./validatorKey02.pem
sleep 2m
pytest -m require_localnet multiversx_sdk_cli/tests
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
ARG PYTHON_VERSION=3.10

# Step 1: Use an official Python runtime as a parent image
FROM python:${PYTHON_VERSION}-slim

# Step 2: Set the working directory in the container
WORKDIR /usr/src/app

# Step 3: Copy the requirements file into the container
COPY requirements.txt ./

# Step 5: Copy the requirements-dev file into the container
COPY requirements-dev.txt ./

# Step 6: Install any dependencies listed in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Step 7: Install build-essential. Will be needed to compile the node binaries.
RUN apt update && apt install -y build-essential

# Step 8: Copy the current directory contents into the container
COPY . .

# Step 9: Setup the localnet configuration in the eventuality of a localnet start.
RUN python -m multiversx_sdk_cli.cli localnet setup --configfile=/usr/src/app/multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml

# Step 10: Specify the entrypoint to run the application
ENTRYPOINT ["python", "-m", "multiversx_sdk_cli.cli"]
229 changes: 229 additions & 0 deletions multiversx_sdk_cli/tests/test_cli_validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import requests
import time
import pytest

from pathlib import Path

from multiversx_sdk_cli.cli import main

testdata_path = Path(__file__).parent / "testdata"
testdata_out = Path(__file__).parent / "testdata-out"

proxy_url = "http://localhost:7950/network/config"
alice_pem = testdata_path / "alice.pem"
reward_address = "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"
bls_key = "e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208"


@pytest.fixture()
def poll_endpoint():
start_time = time.time() # Record the start time
timeout = 600
interval = 1

while True:
try:
# Make the request to the endpoint
response = requests.get(proxy_url, timeout=5) # Add request timeout to prevent blocking indefinitely
if response.status_code == 200:
# Break out of the loop if we get a successful response
return response.json() # Return the response (or .text, .content based on your needs)
else:
print(f"Received non-200 status code: {response.status_code}")

except requests.RequestException as e:
# Handle network exceptions or timeouts
print(f"Request failed: {e}")

# Check if the timeout is reached
if time.time() - start_time > timeout:
print("Polling timed out")
break

# Wait for the specified interval before sending the next request
time.sleep(interval)


@pytest.mark.require_localnet
def test_stake(poll_endpoint):
validators_json = testdata_path / "validators_docker.json"

# Stake with recall nonce
return_code = main([
"validator", "stake",
"--pem", str(alice_pem),
"--value", "2500000000000000000000",
"--validators-file", str(validators_json),
"--reward-address", reward_address,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0

# Stake with provided nonce
return_code = main([
"validator", "stake",
"--pem", str(alice_pem),
"--value", "2500000000000000000000",
"--validators-file", str(validators_json),
"--reward-address", reward_address,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--nonce=0"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_stake_top_up(poll_endpoint):
# Stake with topUp
return_code = main([
"validator", "stake", "--top-up",
"--pem", str(alice_pem),
"--value", "2711000000000000000000",
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_unstake(poll_endpoint):
# Unstake
return_code = main([
"validator", "unstake",
"--pem", str(alice_pem),
"--nodes-public-key", bls_key,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_unbond(poll_endpoint):
# Unbond
return_code = main([
"validator", "unbond",
"--pem", str(alice_pem),
"--nodes-public-key", bls_key,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_unjail(poll_endpoint):
# Unjail
return_code = main([
"validator", "unjail",
"--pem", str(alice_pem),
"--value", "2500000000000000000000",
"--nodes-public-key", bls_key,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_change_reward_address(poll_endpoint):
# Change reward address
return_code = main([
"validator", "change-reward-address",
"--pem", str(alice_pem),
"--reward-address", reward_address,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_unstake_nodes(poll_endpoint):
# Unstake Nodes
return_code = main([
"validator", "unstake-nodes",
"--pem", str(alice_pem),
"--nodes-public-key", bls_key,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_unstake_tokens(poll_endpoint):
# Unstake Tokens
return_code = main([
"validator", "unstake-tokens",
"--pem", str(alice_pem),
"--unstake-value", "11000000000000000000",
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_unbond_nodes(poll_endpoint):
# Unbond nodes
return_code = main([
"validator", "unbond-nodes",
"--pem", str(alice_pem),
"--nodes-public-keys", bls_key,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_unbond_tokens(poll_endpoint):
# Unbond nodes
return_code = main([
"validator", "unbond-tokens",
"--pem", str(alice_pem),
"--unbond-value", "20000000000000000000",
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_clean_registration_data(poll_endpoint):
# Clean registration data
return_code = main([
"validator", "clean-registered-data",
"--pem", str(alice_pem),
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0


@pytest.mark.require_localnet
def test_re_stake_unstaked_nodes(poll_endpoint):
# Clean registration data
return_code = main([
"validator", "restake-unstaked-nodes",
"--pem", str(alice_pem),
"--nodes-public-keys", bls_key,
"--chain", "localnet",
"--proxy", "http://127.0.0.1:7950",
"--estimate-gas", "--recall-nonce"
])
assert return_code == 0
13 changes: 13 additions & 0 deletions multiversx_sdk_cli/tests/testdata/validators_docker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"validators": [
{
"pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/validatorKey00.pem"
},
{
"pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/validatorKey01.pem"
},
{
"pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/validatorKey02.pem"
}
]
}
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
markers =
skip_on_windows: marks tests as being skiped when running on windows (deselect with '-m "skip_on_windows"')
only: only run a specific test (run using: pytest -m "only")
require_localnet: marks tests that require a localnet (run using: pytest -m require_localnet)

log_cli = True
Loading