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

Add pytest integration tests #3038

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8da5866
simple pytest migration
darwintree Nov 12, 2024
ca21e38
refactor: migrate ConfluxTestFramework using pytest's teardown
darwintree Nov 14, 2024
9772e64
refactor: move rpc tests
darwintree Nov 14, 2024
d34c333
refactor: supports parallel testing
darwintree Nov 14, 2024
44c7940
doc: add readme for migrated tests
darwintree Dec 10, 2024
e882b87
Merge pull request #2986 from darwintree/pytest-migration
Pana Dec 11, 2024
991f6a9
ci: replace integration tests
darwintree Dec 12, 2024
a94c10c
Merge pull request #2987 from darwintree/pytest-migration
darwintree Dec 12, 2024
890955f
test: add get_block_by_epoch_number_errors test
iosh Dec 18, 2024
1b6fd65
Update integration_tests/tests/rpc/test_epoch_number.py
darwintree Dec 18, 2024
28bcf56
Merge pull request #3007 from iosh/errors
iosh Dec 18, 2024
3284945
Merge branch 'master' into pytest
darwintree Dec 31, 2024
409a1cb
chore: change util to module, and reuse epoch errors
iosh Dec 31, 2024
db3e158
Merge pull request #3021 from iosh/util
darwintree Dec 31, 2024
2fca800
tests: add example for framework ussage
darwintree Dec 31, 2024
0128f62
Merge pull request #3023 from darwintree/framework-example
Pana Dec 31, 2024
d5a80c7
chore: add block_by_epoch error
iosh Jan 3, 2025
3e44063
chore: add cfx_getTransactionByHash error
iosh Jan 6, 2025
b0ec591
Merge pull request #3029 from iosh/errorTest
iosh Jan 6, 2025
b2bc61f
add basic trace rpc
Pana Jan 2, 2025
0d5e909
refactor: remove asyncore dependency in pytest branch
darwintree Jan 7, 2025
2d13d61
fix: NetworkThread starts multiple times in one worker
darwintree Jan 8, 2025
f5781b1
Merge pull request #3031 from darwintree/remove-asyncore
darwintree Jan 8, 2025
b778e46
Merge branch 'pytest' into test/debugTraceTests
Pana Jan 8, 2025
6984100
optimize code
Pana Jan 8, 2025
a5e4f1a
fix tests
Pana Jan 8, 2025
e4a5fcf
refactor with fixtures
Pana Jan 9, 2025
936cbbe
change name
Pana Jan 9, 2025
eecd2ce
Merge pull request #3030 from Conflux-Chain/test/debugTraceTests
darwintree Jan 9, 2025
2971abb
chore: enable new integration tests in script
darwintree Jan 10, 2025
86ae799
Merge pull request #3035 from darwintree/enable-legacy-and-new-test
darwintree Jan 10, 2025
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ build_clippy/
*~
.DS_Store
tests/artifacts/**/*.dbg.json
venv
.venv
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ Unit tests come together with the Rust code. They can be invoked via `cargo test
[Getting Started](https://doc.confluxnetwork.org/docs/general/run-a-node/)
page for more information.

Integration tests are Python test scripts with the `_test.py` suffix in the `tests` directory.
Integration tests are Python test scripts with the `_test.py` suffix in the `tests` directory and in the `integration_tests/tests` directory.
To run these tests, first compile Conflux in _release_ mode using `cargo build --release` and
fetch all submodule using `git submodule update --remote --recursive --init`.
Then, you can run all integration tests using the script `tests/test_all.py`.
Then, you can run all integration tests using:

- `tests/test_all.py` for tests in the `tests` directory
- `pytest ./integration_tests/tests -vv -n 6 --dist loadscope` for tests in the `integration_tests` directory

## Resources

Expand Down
2 changes: 1 addition & 1 deletion dev-support/dep_pip3.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

set -e

pip3 install cfx-account eth-utils py-ecc rlp trie coincurve safe-pysha3 conflux-web3==1.4.0b5 web3 py-solc-x jsonrpcclient==3.3.6 asyncio websockets pyyaml numpy
pip3 install cfx-account eth-utils py-ecc rlp trie coincurve safe-pysha3 conflux-web3==1.4.0b5 web3 py-solc-x jsonrpcclient==3.3.6 asyncio websockets pyyaml numpy pytest pytest-xdist

python3 -m solcx.install v0.5.17

Expand Down
1 change: 1 addition & 0 deletions dev-support/test-mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ function check_integration_tests {
# Make symbolic link for conflux binary to where integration test assumes its existence.
rm -rf target; ln -s build target
./tests/test_all.py
pytest ./integration_tests/tests -vv -n 6 --dist loadscope
)
local exit_code=$?
popd > /dev/null
Expand Down
1 change: 1 addition & 0 deletions dev-support/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ function check_integration_tests {
# Make symbolic link for conflux binary to where integration test assumes its existence.
rm -f target; ln -s build target
./tests/test_all.py --max-workers $TEST_MAX_WORKERS --max-retries $TEST_MAX_RETRIES | tee /dev/stderr
pytest ./integration_tests/tests -vv -n 6 --dist loadscope | tee /dev/stderr
)
local exit_code=$?
popd > /dev/null
Expand Down
81 changes: 81 additions & 0 deletions docs/integration-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Integration Tests

The Conflux-rust integration tests are written in Python. Currently, there are two versions of the tests located in the `tests` and `integration_tests` directories. All files in these directories with filenames containing `test` are test cases.

The second version refactors the first, making the tests more modular, easier to maintain, and introducing the pytest testing framework.

The integration tests primarily focus on:

- Verifying the correctness of the consensus algorithm
- Ensuring the implementation of each CIP meets the expected specifications
- Validating the correctness of RPC interfaces

## test_framework

The `test_framework` directory contains a blockchain testing framework evolved from Bitcoin's testing framework. This framework allows for setting up a multi-node local test network as needed and provides common blockchain control methods such as block generation, data synchronization, and node termination.

Additionally, the framework offers commonly used testing infrastructure:

- RPC Client
- SDK instantiation
- Commonly used contracts
- Accounts with preloaded balances

## Conflux Utils

The `conflux` directory includes utilities for common blockchain interactions, such as:

- Address conversion
- RPC and pubsub
- Transaction definitions
- Type conversions
- Encoding/decoding

## Contracts

Integration tests require some contracts, primarily located in:

- `tests/contracts`: Solidity code and the corresponding ABI and bytecode files compiled using native compilers.
- `tests/test_contracts`: A Hardhat project containing Solidity files and compiled artifact files ready for use. This directory is a git submodule, with its repository hosted at [conflux-chain/conflux-rust-dev-contracts](https://github.com/Conflux-Chain/conflux-rust-dev-contracts.git).

## SDK

- In version 1 of the integration tests, the Ethereum Python SDK, web3.py, was partially used.
- In version 2, SDKs are extensively used to write test cases. Core Space uses [python-conflux-sdk](https://github.com/Conflux-Chain/python-conflux-sdk), while eSpace uses [web3.py](https://web3py.readthedocs.io/en/stable/index.html).

## Miscellaneous

### Submodules

The integration test framework depends on two git submodules:

- `conflux-rust-dev-contracts`: Contract code
- `extra-test-toolkits`: For consensus and fuzzing tests

Use the following command to fetch the submodule code:

```sh
git submodule update --remote --recursive --init
```

### Node Program

Before running the tests, you must compile the node program. Refer to the [README.md](../README.md) for compilation instructions.

### Running Version 1 Integration Tests

Run all tests:

```sh
python3 tests/test_all.py
```

Run a specific test:

```sh
python3 tests/erc20_test.py
```

### Writing Integration Test Cases

It is recommended to write test cases using the newer version. Older test cases will be gradually migrated. Refer to [integration_tests/readme](../integration_tests/readme.md) for detailed instructions on writing test cases.
Empty file added integration_tests/__init__.py
Empty file.
Empty file.
68 changes: 68 additions & 0 deletions integration_tests/conflux/address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import eth_utils
from eth_utils import decode_hex

from .address_utils import *
from integration_tests.conflux.config import DEFAULT_PY_TEST_CHAIN_ID
from integration_tests.conflux.utils import encode_hex

MAINNET_PREFIX = "cfx"
TESTNET_PREFIX = "cfxtest"
OTHER_NET_PREFIX = "net"
VERSION_BYTE = 0x00
MAINNET_NETWORK_ID = 1029
TESTNET_NETWORK_ID = 1
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"


def network_id_to_prefix(network_id):
if network_id == TESTNET_NETWORK_ID:
return TESTNET_PREFIX
elif network_id == MAINNET_NETWORK_ID:
return MAINNET_PREFIX
else:
return OTHER_NET_PREFIX + str(network_id)


def prefix_to_network_id(prefix):
if prefix == MAINNET_PREFIX:
return MAINNET_NETWORK_ID
elif prefix == TESTNET_PREFIX:
return TESTNET_NETWORK_ID
elif prefix[:3] == OTHER_NET_PREFIX and int(prefix[3:]) not in [TESTNET_NETWORK_ID, MAINNET_NETWORK_ID]:
return int(prefix[3:])
else:
assert False, "Invalid address prefix"


def encode_b32_address(addr, network_id=DEFAULT_PY_TEST_CHAIN_ID):
payload = convertbits([VERSION_BYTE] + list(addr), 8, 5)
prefix = network_id_to_prefix(network_id)
checksum = calculate_checksum(prefix, payload)
return "{}:{}".format(prefix, b32encode(payload + checksum))


# Note: This function does not return network_id on purpose, because in python tests it is DEFAULT_PY_TEST_CHAIN_ID
# while the prefix is `cfx`.
def decode_b32_address(b32_addr):
b32_addr = b32_addr.lower()
addr_array = b32_addr.split(":")
prefix = addr_array[0]
payload_and_checksum = addr_array[-1]
assert len(payload_and_checksum) == 42
payload_and_checksum_raw = b32decode(payload_and_checksum)
if not verify_checksum(prefix, payload_and_checksum_raw):
assert False, "Invalid address checksum"
# Remove checksum bits
payload_raw = payload_and_checksum_raw[:-CHECKSUM_SIZE]
# Remove version byte
address_bytes = bytes(convertbits(payload_raw, 5, 8, pad=False))[1:]
return address_bytes


def b32_address_to_hex(addr):
return eth_utils.encode_hex(decode_b32_address(addr))


def hex_to_b32_address(addr, network_id=DEFAULT_PY_TEST_CHAIN_ID):
return encode_b32_address(decode_hex(addr), network_id)

101 changes: 101 additions & 0 deletions integration_tests/conflux/address_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""
MIT License

Copyright (c) 2017 Shammah Chancellor

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""


BASE32_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789'
EXCLUDE_CHARS = {'o', 'i', 'l', 'q'}
CHARSET = ''
for c in BASE32_CHARS:
if c not in EXCLUDE_CHARS:
CHARSET += c
CHECKSUM_SIZE = 8


def polymod(values):
chk = 1
generator = [
(0x01, 0x98f2bc8e61),
(0x02, 0x79b76d99e2),
(0x04, 0xf33e5fb3c4),
(0x08, 0xae2eabe2a8),
(0x10, 0x1e4f43e470)]
for value in values:
top = chk >> 35
chk = ((chk & 0x07ffffffff) << 5) ^ value
for i in generator:
if top & i[0] != 0:
chk ^= i[1]
return chk ^ 1


def prefix_expand(prefix):
return [ord(x) & 0x1f for x in prefix] + [0]


def calculate_checksum(prefix, payload):
poly = polymod(prefix_expand(prefix) + payload + [0, 0, 0, 0, 0, 0, 0, 0])
out = list()
for i in range(CHECKSUM_SIZE):
out.append((poly >> 5 * (7 - i)) & 0x1f)
return out


def verify_checksum(prefix, payload):
return polymod(prefix_expand(prefix) + payload) == 0


def b32decode(inputs):
out = list()
for letter in inputs:
out.append(CHARSET.find(letter))
return out


def b32encode(inputs):
out = ''
for char_code in inputs:
out += CHARSET[char_code]
return out


def convertbits(data, frombits, tobits, pad=True):
acc = 0
bits = 0
ret = []
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data:
if value < 0 or (value >> frombits):
return None
acc = ((acc << frombits) | value) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
return None
return ret
Loading