Skip to content

Commit

Permalink
test: Added tests for async methods (#13)
Browse files Browse the repository at this point in the history
* test: Added tests for async methods

* chore: bumped version in config
  • Loading branch information
karolpivo authored Jan 2, 2024
1 parent 826b0a5 commit 2871715
Show file tree
Hide file tree
Showing 27 changed files with 980 additions and 58 deletions.
22 changes: 20 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [0.2.0] - 2023-16-22
## [0.3.0] - 2024-01-02

### Added

- e2e tests for async methods
- unit tests for async methods
- example scripts for async methods

### Changed

- Updated documentation
- Updated molecule test scenarios to support Prysm

### Fixed

- Missing class argument in asyncio methods that caused exceptions to be raised


## [0.2.0] - 2023-12-22

### Changed

- Updated documentation

## [0.1.0] - 2023-16-12
## [0.1.0] - 2023-12-16

### Added

Expand Down
106 changes: 104 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Validator clients' native key management is CLI based which does not work well w

The ETH 2 Key Manager API (also referred to as Validator API) enables users to use a single interface to manage keys for all clients. The keystore operations performed using the API do not require the client to be restarted.

## Asynchronous and synchronous methods

The API client provides both asynchronous and synchronous methods. Both methods are implemented using the [httpx](https://www.python-httpx.org/) library, which uses AsyncIO under the hood. The asynchronous methods let you perform operation on multiple validators in non-blocking manner. This is significantly faster when managing large number of validators. Refer to [examples](https://eth-2-key-manager-api-client.slingnode.com/) for sample scripts.


## Resources

The Full documentation is available at [https://eth-2-key-manager-api-client.slingnode.com/](https://eth-2-key-manager-api-client.slingnode.com/)
Expand Down Expand Up @@ -66,7 +71,7 @@ eth_2_key_manager = eth_2_key_manager_api_client.Eth2KeyManager(base_url="http:/

For full list of examples refer to [examples](https://eth-2-key-manager-api-client.slingnode.com/).

## Import keystores
## Import keystores - synchronous

```python
import eth_2_key_manager_api_client
Expand Down Expand Up @@ -112,7 +117,7 @@ assert response.status_code == 200

```

## List keys
## List keys - synchronous

```python
import eth_2_key_manager_api_client
Expand All @@ -132,3 +137,100 @@ else:
print(f"List keys failed with status code: {response.status_code}")

```

## Import remote keys - asynchronous

NOTE: for legibility this example imports the same remote keys to multiple validators. We would never do that in a real world scenario.

```python
import asyncio
import eth_2_key_manager_api_client

validators = [
(
"LIGHTHOUSE",
"http://192.168.121.71:7500",
"api-token-0x024fa0a0c597e83a970e689866703a89a555c9ee602d08cf848ee7935509a62f33"
),
(
"TEKU",
"https://192.168.121.245:7500",
"5b874584cecab7eadfc3a224f177272f"
),
(
"NIMBUS",
"http://192.168.121.61:7500",
"fcb4869b587a71b6d7ff25c85ac312201e53"
),
(
"LODESTAR",
"http://192.168.121.118:7500",
"api-token-0xff6b738e030ee41e6bc317c204e2dae8965f6d666dc158e5690940936eeb35d9"
)
]

remote_keys = [
{
"pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
"url": "https://remote.signer"
}
]


async def import_remote_keys_async(validator):

eth_2_key_manager = eth_2_key_manager_api_client.Eth2KeyManager(
base_url=validator[1],
token=validator[2]
)

response = await eth_2_key_manager.import_remote_keys.asyncio_detailed(remote_keys=remote_keys)

if response.status_code == 200:
print(f"{validator[0]} - {validator[1]} - Remote keys imported successfully")
else:
print(f"{validator[0]} - {validator[1]} - Remote keys import failed with status code: {response.status_code}")


async def main():
await asyncio.gather(*(import_remote_keys_async(validator) for validator in validators))


asyncio.run(main())
```

## List keys - asynchronous

```python
import asyncio
import eth_2_key_manager_api_client
from tests.conftest import parse_file

validators = parse_file("../.env")


async def list_keys_async(validator):

eth_2_key_manager = eth_2_key_manager_api_client.Eth2KeyManager(
base_url=validator[1],
token=validator[2]
)

response = await eth_2_key_manager.list_keys.asyncio_detailed()

if response.status_code == 200:
print(f"{validator[0]} - {validator[1]} - Number of keys: {len(response.parsed.data)}")
if response.parsed.data:
print("List of keys:")
for key in response.parsed.data:
print(f" {key.validating_pubkey}")
else:
print(f"{validator[0]} - {validator[1]} - List keys failed with status code: {response.status_code}")


async def main():
await asyncio.gather(*(list_keys_async(validator) for validator in validators))


asyncio.run(main())
```
59 changes: 53 additions & 6 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,53 @@ The example scripts are located in the examples directory.

# Local Key Manager

## Import keystores
## Import keystores - synchronous

```python
--8<--
examples/import_keystores.py
--8<--
```
## Import keystores - async

## List keys
```python
--8<--
examples/import_keystores_async.py
--8<--
```

## List keys - synchronous

```python
--8<--
examples/list_keys.py
--8<--
```

## Delete keys
## List keys - async

```python
--8<--
examples/list_keys_async.py
--8<--
```

## Delete keys - synchronous

```python
--8<--
examples/delete_keys.py
--8<--
```

## Delete keys - async

```python
--8<--
examples/delete_keys_async.py
--8<--
```

# Gas limit

## Set gas limit
Expand Down Expand Up @@ -83,26 +106,50 @@ examples/delete_fee_recipient.py

# Remote Key Manager

## Import remote keys
## Import remote keys - synchronous

```python
--8<--
examples/import_remote_keys.py
--8<--
```

## List remote keys
## Import remote keys - async

```python
--8<--
examples/import_remote_keys_async.py
--8<--
```

## List remote keys - synchronous

```python
--8<--
examples/list_remote_keys.py
--8<--
```

## List remote keys - async

```python
--8<--
examples/list_remote_keys.py
--8<--
```

## Delete remote keys
## Delete remote keys - synchronous

```python
--8<--
examples/delete_remote_keys.py
--8<--
```

## Delete remote keys - async

```python
--8<--
examples/delete_remote_keys_async.py
--8<--
```
27 changes: 24 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Validator clients' native key management is CLI based which does not work well w

The ETH 2 Key Manager API (also referred to as Validator API) enables users to use a single interface to manage keys for all clients. The keystore operations performed using the API do not require the client to be restarted.

## Asynchronous and synchronous methods

The API client provides both asynchronous and synchronous methods. Both methods are implemented using the [httpx](https://www.python-httpx.org/) library, which uses AsyncIO under the hood. The asynchronous methods let you perform operation on multiple validators in non-blocking manner. This is significantly faster when managing large number of validators. Refer to [examples](https://eth-2-key-manager-api-client.slingnode.com/) for sample scripts.


## Resources

The Full documentation is available at [https://eth-2-key-manager-api-client.slingnode.com/](https://eth-2-key-manager-api-client.slingnode.com/)
Expand Down Expand Up @@ -64,22 +69,38 @@ eth_2_key_manager = eth_2_key_manager_api_client.Eth2KeyManager(base_url="http:/

# Examples


For full list of examples refer to [examples](examples.md)

This module provides synchronous and asynchronous methods for all API endpoints. The synchronous methods are blocking and will return the response object when the request is complete. The asynchronous methods are implemented using AsyncIO and will return a coroutine object that can be awaited.

## Import keystores
## Import keystores - synchronous

```python
--8<--
examples/import_keystores.py
--8<--
```

## List keys
## List keys - synchronous

```python
--8<--
examples/list_keys.py
--8<--
```

## Import remote keys - asynchronous

```python
--8<--
examples/import_remote_keys_async.py
--8<--
```

## List keys - asynchronous

```python
--8<--
examples/list_keys_async.py
--8<--
```
38 changes: 37 additions & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,49 @@ VALIDATOR_NAME BASE_URL API_TOKEN
For example:

```bash
TEKU http://192.168.121.35:7500 410ef40da53b447f76ec52fa43092032
TEKU https://192.168.121.35:7500 410ef40da53b447f76ec52fa43092032
LIGHTHOUSE http://192.168.121.189:7500 api-token-0x022b0c73c4757866df53b9b24a5b254222daca269e8be677c1efb15a93aba148a7
NIMBUS http://192.168.121.42:7500 06ab97c6170c1ae09bf3eb69a300d2795fb6
LODESTAR http://192.168.121.57:7500 api-token-0x9e75d2fb89d9a8230d027665ad0f67b777aaa2f1d23f282125001d0dea753901
PRYSM http://192.168.121.53:7500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.orijiINlQIwSlB5ZxjmzAEqlYtI3SUNC6ennFnbcVCs
```

Sample output:

```bash
python -m pytest tests/e2e/test_e2e_api_fee_recipient_async.py -s
==================================================== test session starts ====================================================
platform linux -- Python 3.10.12, pytest-7.4.3, pluggy-1.3.0
rootdir: /home/kp/projects/slingnode/eth-2-key-manager-api-client
plugins: mock-3.12.0, cov-4.1.0, httpx-0.26.0, asyncio-0.21.1, anyio-4.1.0
asyncio: mode=strict
collected 4 items

tests/e2e/test_e2e_api_fee_recipient_async.py
Validator client -> LIGHTHOUSE
Fee Recipient set successfully
Fee recipient for pubkey 0x99c4c42fac7d1393956bd9e2785ed67cf5aaca4bf56d2fcda94c42d6042aebb1723ce6bac6f0216ff8c5d4f9f013008b is 0xabcf8e0d4e9587369b2301d0790347320302cc09
Fee Recipient deleted successfully
.
Validator client -> TEKU
Fee Recipient set successfully
Fee recipient for pubkey 0x99c4c42fac7d1393956bd9e2785ed67cf5aaca4bf56d2fcda94c42d6042aebb1723ce6bac6f0216ff8c5d4f9f013008b is 0xAbcF8e0d4e9587369b2301D0790347320302cc09
Fee Recipient deleted successfully
.
Validator client -> NIMBUS
Fee Recipient set successfully
Fee recipient for pubkey 0x99c4c42fac7d1393956bd9e2785ed67cf5aaca4bf56d2fcda94c42d6042aebb1723ce6bac6f0216ff8c5d4f9f013008b is 0xabcf8e0d4e9587369b2301d0790347320302cc09
Fee Recipient deleted successfully
.
Validator client -> LODESTAR
Fee Recipient set successfully
Fee recipient for pubkey 0x99c4c42fac7d1393956bd9e2785ed67cf5aaca4bf56d2fcda94c42d6042aebb1723ce6bac6f0216ff8c5d4f9f013008b is 0xabcf8e0d4e9587369b2301d0790347320302cc09
Fee Recipient deleted successfully
.

==================================================== 4 passed in 13.87s =====================================================
```

# Testing using Ansible Molecule

The project includes a Molecule test suite that can be used to test the API client against a real Ethereum Validator client. Molecule is a testing framework for Ansible roles. The test suite is located in the `molecule` directory. The test suite uses SlingNode Ethereum Ansible roles to set up the Ethereum Validator clients. The included test suite will automatically create the .env file and output the details required by the pytest e2e tests.
Expand Down
Loading

0 comments on commit 2871715

Please sign in to comment.