-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Structure improvement functional tests (#309)
* added pyproject.toml/poetry.lock and change on README to apply proposition made on issue#252 defined python 3.12 as minimum version for functional tests * linted tests/{example_test, restart, run_tests}.py changed example_test I: added assertions at run_test changed example_test II: added set_params method changed restart: applied the main call at the end of file changed run_tests: flexibilized it for individual tests modified pyproject.toml I: added individual test tasks (example-test, restart-test) modified pyproject.toml II: defined tests task as a sequence of individual tests linted tests/tests_framework/{utreexod, floresta_rpc, test_framework, secp256k1, electrum_client, mock_rpc, key, bitcoin}.py changed floresta_rpc I: diminished the number of arguments of FlorestaRPC changed floresta_rpc II: created a default value REGTEST_RPC_SERVER changed test_framework I: create class FlorestaTestMetaClass changed test_framework II: renamed the class as FlorestaTestFramework changed test_framework III: modified run_node method due to changes on tests/tests_framework/floresta.py changed test_framework IV: added methods (set_test_params, add_node_settings, get_node_settings) changed secp256k1 I: disabled linter on some lines changed secp256k1 II: moved to tests/test_framework/crypto to comply with definition of modules in key.py changed bitcoin I: added methods (dsha256, read_compact_size method, get_merkle_root method, Transaction.deserialize) changed bitcoin II: modified Block.get_merkle_root * changed .github/workflows/functional.yml to comply with the functional tests changes --------- Co-authored-by: qlrd <qlrddev [email protected]>
- Loading branch information
Showing
15 changed files
with
1,958 additions
and
461 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# With this the user can use a custom | ||
# python environment, like those created | ||
# with venv or pyenv. The default will be | ||
# the system python. | ||
[virtualenvs] | ||
prefer-active-python = true | ||
|
||
# This isn't really needed on pratical aspect | ||
# since this is only for organize the functional tests | ||
# but is necessary since its a requirement for poetry. | ||
[tool.poetry] | ||
name = "floresta-functional-tests" | ||
version = "0.0.1" | ||
description = "collection of tools to help with functional testing of Floresta" | ||
authors = ["The Floresta Project Developers"] | ||
license = "MIT" | ||
|
||
# To add a new dependency, use `poetry add <package>`. | ||
# To update, first find outdated packages with `poetry show --outdated`, | ||
# then re-install with `poetry add <package>@<version>`. | ||
[tool.poetry.dependencies] | ||
python = ">=3.12" | ||
jsonrpclib = "^0.2.1" | ||
requests = "^2.32.3" | ||
black = "^24.10.0" | ||
pylint = "^3.3.2" | ||
poethepoet = "^0.31.1" | ||
|
||
# Here we can define some custom tasks | ||
# (even bash or rust ones can be defined). | ||
# All of them will run on virtualenv environment. | ||
# For more information, see guidelines in https://poethepoet.natn.io | ||
[tool.poe.tasks] | ||
format = "black ./tests" | ||
lint = "pylint ./tests" | ||
example-test = "python tests/run_tests.py --test-name example_test" | ||
restart-test = "python tests/run_tests.py --test-name restart" | ||
tests = ["example-test", "restart-test"] | ||
pre-commit = ["format", "lint", "tests"] | ||
|
||
[build-system] | ||
requires = ["poetry-core"] | ||
build-backend = "poetry.core.masonry.api" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,116 @@ | ||
""" | ||
This is an example of how tests should look like, see the class bellow for more info | ||
example_test.py | ||
This is an example of how tests should look like, see the class bellow for more info. | ||
Every test should define, at least, two special methods: | ||
- `set_test_params`: change default values for number of node, topology, etc. | ||
- `run_test`: the test itself | ||
Any attempt to define a test without these methods will raise a TypeError. | ||
Other methods are available to do more things. Some of them make sense only in `set_test_params` | ||
and others make sense only in `run_tests`. | ||
- `add_node_settings`: register a node settings to create a node after. A node will be a spawned | ||
`cargo run --features json-rpc --bin florestad -- --network <chain>` process. | ||
In summary, its a `FlorestaRPC` instance. | ||
The chain can be one of ["regtest", "signet", "testnet"]. | ||
You can pass some extra arguments with `extra_args` and it will be appended | ||
to the process command. The `rpcserver` is a dictionary defining a "host", | ||
"port", "username" and "password". The "data_dir" is optional and can be | ||
used to create a temporary directory to store files. It will return an | ||
integer pointing an index of a list of nodes. | ||
- `get_node_settings`: get a registered node settings | ||
- `run_node`: run a node for a registered node settings at some index, | ||
configured with `add_node_settings`. | ||
- `get_node`: get a resgistered running node. | ||
- `wait_for_rpc_connection`: given a node index, wait for it to be available. | ||
- `run_rpc`: our RPC is a MockUtreexod running on a thread. | ||
- `stop_node`: given a running node at index, stop it. | ||
If any directory was created, it will be removed. | ||
- `stop`: stop all registered nodes. | ||
After the definition of test within a class, you should call `MyTest().main()` at the end of file. | ||
""" | ||
import time | ||
import os | ||
|
||
from test_framework.test_framework import TestFramework | ||
import json | ||
from test_framework.test_framework import FlorestaTestFramework | ||
from test_framework.electrum_client import ElectrumClient | ||
from test_framework.mock_rpc import MockUtreexod | ||
from test_framework.floresta_rpc import REGTEST_RPC_SERVER | ||
|
||
|
||
class ExampleTest(FlorestaTestFramework): | ||
""" | ||
Tests should be a child class from FlorestaTestFramework | ||
class ExampleTest(TestFramework): | ||
""" Tests should be a child class from TestFramework """ | ||
In each test class definition, `set_test_params` and `run_test`, say what | ||
the test do and the expected result in the docstrings | ||
""" | ||
|
||
index = [-1] | ||
expected_version = ["Floresta 0.3.0", "1.4"] | ||
expected_height = 0 | ||
expected_block = "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206" | ||
expected_difficulty = 1 | ||
expected_leaf_count = 0 | ||
|
||
def set_test_params(self): | ||
""" | ||
Here we define setup for test adding a node definition | ||
""" | ||
ExampleTest.index[0] = self.add_node_settings( | ||
chain="regtest", extra_args=[], rpcserver=REGTEST_RPC_SERVER | ||
) | ||
|
||
# All tests should override the run_test method | ||
def run_test(self): | ||
""" | ||
Here we define the test itself: | ||
- creates a dummy rpc listening on port 8080 | ||
- wait until the rpc is ready and start a new node (this crate's binary) | ||
- wait the node to start | ||
- perform some requests to FlorestaRPC node | ||
- Create an instance of the Electrum Client, a small implementation of the electrum | ||
protocol, to test our own electrum implementation | ||
""" | ||
# This creates a dummy rpc listening on port 8080 | ||
self.run_rpc() | ||
|
||
# Wait until the rpc is ready | ||
# Start a new node (this crate's binary) | ||
node1 = self.run_node("./data/test1", "regtest") | ||
self.run_node(ExampleTest.index[0]) | ||
|
||
# Wait the node to start | ||
self.wait_for_rpc_connection() | ||
self.wait_for_rpc_connection(ExampleTest.index[0]) | ||
|
||
# Perform for some defined requests to FlorestaRPC | ||
node = self.get_node(ExampleTest.index[0]) | ||
inf_response = node.get_blockchain_info() | ||
|
||
# Create an instance of the Electrum Client, a small implementation of the electrum | ||
# protocol, to test our own electrum implementation | ||
electrum = ElectrumClient("localhost", 50001) | ||
print(electrum.get_version()) | ||
rpc_response = json.loads(electrum.get_version()) | ||
|
||
# Make assertions | ||
assert rpc_response["result"][0] == ExampleTest.expected_version[0] | ||
assert rpc_response["result"][1] == ExampleTest.expected_version[1] | ||
assert inf_response["height"] == ExampleTest.expected_height | ||
assert inf_response["best_block"] == ExampleTest.expected_block | ||
assert inf_response["difficulty"] == ExampleTest.expected_difficulty | ||
assert inf_response["leaf_count"] == ExampleTest.expected_leaf_count | ||
|
||
|
||
if __name__ == '__main__': | ||
if __name__ == "__main__": | ||
ExampleTest().main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,65 @@ | ||
import subprocess | ||
""" | ||
restart.py | ||
A simple test that restart a Floresta node and a related data directory. | ||
The directories used between each power-on/power-off must not be corrupted. | ||
""" | ||
|
||
import time | ||
import os | ||
import filecmp | ||
from test_framework.test_framework import TestFramework | ||
from test_framework.test_framework import FlorestaTestFramework | ||
from test_framework.floresta_rpc import REGTEST_RPC_SERVER | ||
|
||
|
||
class TestRestart(FlorestaTestFramework): | ||
""" | ||
Test the restart of a node, calling a first node (0) the recall it as (1); | ||
We need to check if given data_dirs arent corrupted between restarts | ||
""" | ||
|
||
indexes = [-1, -1] | ||
|
||
def set_test_params(self): | ||
""" | ||
Here we define setup for test | ||
""" | ||
dirname = os.path.dirname(__file__) | ||
TestRestart.indexes[0] = self.add_node_settings( | ||
chain="regtest", | ||
extra_args=[], | ||
rpcserver=REGTEST_RPC_SERVER, | ||
data_dir=os.path.normpath(os.path.join(dirname, "data", "0")), | ||
) | ||
TestRestart.indexes[1] = self.add_node_settings( | ||
chain="regtest", | ||
extra_args=[], | ||
rpcserver=REGTEST_RPC_SERVER, | ||
data_dir=os.path.normpath(os.path.join(dirname, "data", "1")), | ||
) | ||
|
||
class TestRestart(TestFramework): | ||
def run_test(self): | ||
""" | ||
Tests if we don't corrupt our data dir between restarts. This would have caught, | ||
the error fixed in #9 | ||
Tests if we don't corrupt our data dir between restarts. This would have caught, | ||
the error fixed in #9 | ||
""" | ||
base_testdir = "data/TestRestart/" | ||
self.run_node(base_testdir + "1/") | ||
time.sleep(5) | ||
self.stop_node(0) | ||
self.run_node(base_testdir + "2/") | ||
time.sleep(5) | ||
self.stop_node(0) | ||
assert (filecmp.dircmp(base_testdir + "2/", base_testdir + "1/")) | ||
# start first node, wait and then kill | ||
self.run_node(TestRestart.indexes[0]) | ||
time.sleep(5.0) | ||
self.stop_node(TestRestart.indexes[0]) | ||
|
||
# start second node, wait and then kill | ||
self.run_node(TestRestart.indexes[1]) | ||
time.sleep(5.0) | ||
self.stop_node(TestRestart.indexes[1]) | ||
|
||
# check for any corruption | ||
assert filecmp.dircmp( | ||
self.get_node_settings(TestRestart.indexes[0])["data_dir"], | ||
self.get_node_settings(TestRestart.indexes[1])["data_dir"], | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
TestRestart().main() |
Oops, something went wrong.