diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index cea43fa..43247c1 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -22,7 +22,7 @@ jobs: export USE_TESTNET=1 && export ENS_ACCOUNT_NAME=${{secrets.ENS_ACCOUNT_NAME}} && export ENS_ACCOUNT_SECRET=${{secrets.ENS_ACCOUNT_SECRET}} && - pytest --cov tests + pytest --cov tests -n logical --dist loadgroup - name: Upload coverage reports to Codecov run: | curl -Os https://uploader.codecov.io/latest/linux/codecov diff --git a/.github/workflows/local-test.yml b/.github/workflows/local-test.yml index 1ce054d..19d0ea0 100644 --- a/.github/workflows/local-test.yml +++ b/.github/workflows/local-test.yml @@ -28,4 +28,4 @@ jobs: pip install ".[tester]" - name: Run test run: | - pytest tests + pytest tests -n logical --dist loadgroup diff --git a/.github/workflows/testnet-test.yml b/.github/workflows/testnet-test.yml index 1992909..485af17 100644 --- a/.github/workflows/testnet-test.yml +++ b/.github/workflows/testnet-test.yml @@ -33,4 +33,4 @@ jobs: export ENS_ACCOUNT_NAME=${{secrets.ENS_ACCOUNT_NAME}} && export ENS_ACCOUNT_SECRET=${{secrets.ENS_ACCOUNT_SECRET}} && export TEST_FINALIZATION=1 && - pytest tests + pytest tests -n logical --dist loadgroup diff --git a/pyproject.toml b/pyproject.toml index 6999ed5..18edbe5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ typeCheckingMode = "strict" exclude=["build/*"] -venv="venv" +venv="sdk-313" reportUnknownMemberType = "information" reportUnknownVariableType = "warning" reportUnknownArgumentType = "information" @@ -14,3 +14,4 @@ reportIncompatibleVariableOverride = "information" reportMissingTypeStubs = "information" reportPrivateImportUsage = "information" reportPrivateUsage = "information" +reportUntypedFunctionDecorator = "information" diff --git a/setup.py b/setup.py index 15507d0..b165f1b 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ 'tester': [ "docker>=7.1.0,<8", "pytest>=8,<9", + "pytest-xdist>=3,<4", "typing_extensions", "pytest-cov", "ipfshttpclient==0.8.0a2", diff --git a/tests/base_features/test_default_account.py b/tests/base_features/test_default_account.py index f69d382..be9e74f 100644 --- a/tests/base_features/test_default_account.py +++ b/tests/base_features/test_default_account.py @@ -1,5 +1,4 @@ from conflux_web3 import Web3 -from web3.datastructures import AttributeDict def test_default_account_set(w3: Web3, secret_key): local_account = w3.account.from_key(secret_key) diff --git a/tests/cns/test_cns.py b/tests/cns/test_cns.py index eb849d7..f851d83 100644 --- a/tests/cns/test_cns.py +++ b/tests/cns/test_cns.py @@ -30,7 +30,7 @@ def test_cns_from_address(use_testnet: bool, ens_name: str, ens_account: LocalAc w3 = Web3(provider, cns=cns) assert w3.cns.address(ens_name) == ens_account.address - + def test_cns_with_rpc(w3: Web3, use_testnet: bool, ens_name: str): if use_testnet: @@ -39,7 +39,8 @@ def test_cns_with_rpc(w3: Web3, use_testnet: bool, ens_name: str): else: with pytest.raises(NameServiceNotSet): balance = w3.cfx.get_balance("hello45678oiuytrrtyuiytredcv.web3") - + +@pytest.mark.xdist_group(name="account") def test_cns_usage_as_contract_param(w3: Web3, to_test_cns_write_api: bool, account: LocalAccount, ens_name: str): if to_test_cns_write_api: w3.cfx.default_account = account @@ -50,6 +51,7 @@ def test_cns_usage_as_contract_param(w3: Web3, to_test_cns_write_api: bool, acco assert erc20.functions.transfer(ens_name, 100).transact().executed() assert erc20.caller.balanceOf(ens_name) == 100 +@pytest.mark.xdist_group(name="account") def test_cns_as_sender(w3: Web3, to_test_cns_write_api: bool, ens_account: LocalAccount, ens_name: bool): if to_test_cns_write_api: w3.wallet.add_account(ens_account) @@ -59,6 +61,7 @@ def test_cns_as_sender(w3: Web3, to_test_cns_write_api: bool, ens_account: Local "from": ens_name }).executed() +@pytest.mark.xdist_group(name="account") def test_cns_as_contract_address(w3: Web3, to_test_cns_write_api: bool): if to_test_cns_write_api: faucet = w3.cfx.contract("faucet.web3", name="Faucet", with_deployment_info=False) @@ -81,6 +84,7 @@ def test_cns_owner(w3: Web3, use_testnet: bool, ens_name: str): # w3.cfx.default_account = account # w3.cns.setup_owner("test.web3", wrapped=True) +@pytest.mark.xdist_group(name="account") def test_setup_address(w3: Web3, to_test_cns_write_api: bool, ens_account: LocalAccount): if to_test_cns_write_api: w3.cns.allow_unstable_api = True @@ -99,6 +103,7 @@ def test_cns_wallet(w3: Web3, use_testnet: bool): if use_testnet: assert w3.wallet is w3.cns.w3.wallet +@pytest.mark.xdist_group(name="account") def test_cns_default_account(w3: Web3, use_testnet: bool, account: LocalAccount): if use_testnet: w3.cfx.default_account = account diff --git a/tests/conftest.py b/tests/conftest.py index cd8f168..4a649b5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -57,7 +57,7 @@ def w3(node_url: str, node: LocalNode) -> Web3: return w3 @pytest.fixture(scope="session") -def account(node_url: str, secret_key) -> LocalAccount: +def account(node_url: str, secret_key: str) -> LocalAccount: """external_account, not supported by node """ provider = Web3.HTTPProvider(node_url) diff --git a/tests/middleware/test_pending.py b/tests/middleware/test_pending.py index 583cda8..01ca643 100644 --- a/tests/middleware/test_pending.py +++ b/tests/middleware/test_pending.py @@ -7,32 +7,33 @@ if TYPE_CHECKING: from conflux_web3 import Web3 -def test_pending(w3: "Web3", account: LocalAccount, use_testnet: bool): - # activate by default - # w3.middleware_onion.add(PendingTransactionMiddleware) +# @pytest.mark.xdist_group(name="account") +# def test_pending(w3: "Web3", account: LocalAccount, use_testnet: bool): +# # activate by default +# # w3.middleware_onion.add(PendingTransactionMiddleware) - status = w3.cfx.get_status() - addr = account.address +# status = w3.cfx.get_status() +# addr = account.address - tx = { - 'from': addr, - 'nonce': w3.cfx.get_next_nonce(addr), - 'gas': 21000, - 'to': w3.cfx.account.create().address, - 'value': 100, - 'maxFeePerGas': 2 * w3.cfx.gas_price, - 'maxPriorityFeePerGas': 0, - 'chainId': w3.cfx.chain_id, - 'storageLimit': 0, - 'epochHeight': status['epochNumber'] - } - signed = account.sign_transaction(tx) - rawTx = signed.raw_transaction - pending = w3.cfx.send_raw_transaction(rawTx) - # hash = cast(PendingTransaction, hash) - pending.mined() - pending.executed() - pending.confirmed() - if use_testnet and os.environ.get("TEST_FINALIZATION", None): - with pytest.warns(UserWarning): - pending.finalized() +# tx = { +# 'from': addr, +# 'nonce': w3.cfx.get_next_nonce(addr), +# 'gas': 21000, +# 'to': w3.cfx.account.create().address, +# 'value': 100, +# 'maxFeePerGas': 2 * w3.cfx.gas_price, +# 'maxPriorityFeePerGas': 0, +# 'chainId': w3.cfx.chain_id, +# 'storageLimit': 0, +# 'epochHeight': status['epochNumber'] +# } +# signed = account.sign_transaction(tx) +# rawTx = signed.raw_transaction +# pending = w3.cfx.send_raw_transaction(rawTx) +# # hash = cast(PendingTransaction, hash) +# pending.mined() +# pending.executed() +# pending.confirmed() +# if use_testnet and os.environ.get("TEST_FINALIZATION", None): +# with pytest.warns(UserWarning): +# pending.finalized() diff --git a/tests/middleware/test_wallet.py b/tests/middleware/test_wallet.py index 92496eb..431fa3a 100644 --- a/tests/middleware/test_wallet.py +++ b/tests/middleware/test_wallet.py @@ -9,7 +9,7 @@ construct_sign_and_send_raw_middleware ) - +@pytest.mark.xdist_group(name="account") def test_wallet_middleware_single_init(w3:Web3, account: LocalAccount): wallet = construct_sign_and_send_raw_middleware(account, w3.cfx.chain_id) w3.middleware_onion.add(wallet) @@ -28,6 +28,7 @@ def test_wallet_middleware_single_init(w3:Web3, account: LocalAccount): assert hash w3.cfx.wait_for_transaction_receipt(hash) +@pytest.mark.xdist_group(name="account") def test_no_chain_id_wallet_middleware_single_init(w3:Web3, account: LocalAccount): wallet = construct_sign_and_send_raw_middleware(account) w3.middleware_onion.add(wallet) @@ -46,6 +47,7 @@ def test_no_chain_id_wallet_middleware_single_init(w3:Web3, account: LocalAccoun assert hash w3.cfx.wait_for_transaction_receipt(hash) +@pytest.mark.xdist_group(name="account") def test_wallet_middleware_list_init(w3:Web3, account: LocalAccount): wallet = Wallet([account], w3.cfx.chain_id) w3.middleware_onion.add(wallet) @@ -64,6 +66,7 @@ def test_wallet_middleware_list_init(w3:Web3, account: LocalAccount): assert hash w3.cfx.wait_for_transaction_receipt(hash) +@pytest.mark.xdist_group(name="account") def test_wallet_middleware_adding(w3: Web3, account: LocalAccount): wallet = Wallet(forced_chain_id=w3.cfx.chain_id) wallet.add_accounts([account]) @@ -83,6 +86,7 @@ def test_wallet_middleware_adding(w3: Web3, account: LocalAccount): assert hash w3.cfx.wait_for_transaction_receipt(hash) +@pytest.mark.xdist_group(name="account") def test_default_wallet_middleware_adding(w3: Web3, account: LocalAccount): w3.wallet.add_accounts([account]) tx = { @@ -154,7 +158,7 @@ def test_wallet_pop(): assert wallet.pop(account.address).address == account.address assert account.address not in wallet - +@pytest.mark.xdist_group(name="account") def test_wallet_middleware_sign_1559_transaction(w3:Web3, account: LocalAccount): wallet = construct_sign_and_send_raw_middleware(account, w3.cfx.chain_id) w3.middleware_onion.add(wallet) @@ -174,6 +178,7 @@ def test_wallet_middleware_sign_1559_transaction(w3:Web3, account: LocalAccount) assert tx_data['type'] == 2 hash.executed() +@pytest.mark.xdist_group(name="account") def test_wallet_middleware_sign_legacy_transaction(w3:Web3, account: LocalAccount): wallet = construct_sign_and_send_raw_middleware(account, w3.cfx.chain_id) w3.middleware_onion.add(wallet) diff --git a/tests/rpcs/cfx/filter/test_log_filter.py b/tests/rpcs/cfx/filter/test_log_filter.py index 0f2c1df..2bb87f0 100644 --- a/tests/rpcs/cfx/filter/test_log_filter.py +++ b/tests/rpcs/cfx/filter/test_log_filter.py @@ -12,6 +12,7 @@ def contract(self, moduled_w3: Web3): assert contract_address is not None return w3.cfx.contract(contract_address, name="ERC20") + @pytest.mark.xdist_group(name="account") def test_log_filter(self, moduled_w3: Web3, contract: ConfluxContract): log_filter_id = moduled_w3.cfx.new_filter(address = contract.address) contract.functions.transfer(contract.address, 1**18).transact().executed() diff --git a/tests/rpcs/cfx/filter/test_pending_tx_filter.py b/tests/rpcs/cfx/filter/test_pending_tx_filter.py index 71cd5bf..a38c2c8 100644 --- a/tests/rpcs/cfx/filter/test_pending_tx_filter.py +++ b/tests/rpcs/cfx/filter/test_pending_tx_filter.py @@ -1,8 +1,10 @@ +import pytest from conflux_web3 import Web3 from conflux_web3.types import Drip from tests._test_helpers.type_check import TypeValidator class TestPendingTxFilter: + @pytest.mark.xdist_group(name="account") def test_pending_tx_filter(self, moduled_w3: Web3): pending_tx_filter_id = moduled_w3.cfx.new_pending_transaction_filter() constucted_pending_tx = moduled_w3.cfx.send_transaction({ diff --git a/tests/rpcs/cfx/test_cfx_account_query_rpcs.py b/tests/rpcs/cfx/test_cfx_account_query_rpcs.py index 4a1f9b2..8f8ba24 100644 --- a/tests/rpcs/cfx/test_cfx_account_query_rpcs.py +++ b/tests/rpcs/cfx/test_cfx_account_query_rpcs.py @@ -5,6 +5,7 @@ from tests._test_helpers.type_check import TypeValidator class TestAccountQuery: + @pytest.mark.xdist_group(name="account") def test_get_balance(self, w3: Web3, address: Base32Address): balance = w3.cfx.get_balance(address, w3.cfx.epoch_number-5) # the balance is supposed to be non-zero @@ -18,12 +19,14 @@ def test_get_balance(self, w3: Web3, address: Base32Address): # with pytest.raises(TypeError): # w3.cfx.get_balance() + @pytest.mark.xdist_group(name="account") def test_get_staking_balance(self, w3: Web3, address: Base32Address): staking_balance = w3.cfx.get_staking_balance(address, w3.cfx.epoch_number-5) assert staking_balance >= 0 assert isinstance(staking_balance, Drip) # TODO: use staking balance contract - + + @pytest.mark.xdist_group(name="account") def test_get_code(self, w3: Web3, contract_address: Base32Address): # test different cases # contract address / user address @@ -33,6 +36,7 @@ def test_get_code(self, w3: Web3, contract_address: Base32Address): user_code = w3.cfx.get_code(w3.cfx.account.create().address) assert user_code == HexBytes("0x") + @pytest.mark.xdist_group(name="account") def test_get_admin(self, w3: Web3, contract_address: Base32Address): # test different cases # contract address / user address @@ -43,6 +47,7 @@ def test_get_admin(self, w3: Web3, contract_address: Base32Address): user_admin = w3.cfx.get_admin(random_contract_address) assert user_admin is None + @pytest.mark.xdist_group(name="account") def test_get_storage_at(self, w3: Web3, contract_address: Base32Address, use_testnet: bool): # TODO: a potential bug in RPC, at present we ignore the testing in local node if use_testnet: @@ -51,6 +56,7 @@ def test_get_storage_at(self, w3: Web3, contract_address: Base32Address, use_tes else: pass + @pytest.mark.xdist_group(name="account") def test_get_storage_root(self, w3: Web3, contract_address: Base32Address): root = w3.cfx.get_storage_root(contract_address, w3.cfx.epoch_number_by_tag("latest_state")) TypeValidator.validate_typed_dict(root, "StorageRoot") @@ -58,26 +64,31 @@ def test_get_storage_root(self, w3: Web3, contract_address: Base32Address): # TODO: check RPC work pattern # root = w3.cfx.get_storage_root(w3.account.create().address) # assert not root - + + @pytest.mark.xdist_group(name="account") def test_get_collateral_for_storage(self, w3: Web3, address: Base32Address): storage = w3.cfx.get_collateral_for_storage(address, w3.cfx.epoch_number_by_tag("latest_state")) assert isinstance(storage, int) + @pytest.mark.xdist_group(name="account") def test_get_sponsor_info(self, w3: Web3, contract_address: Base32Address): sponsor_info = w3.cfx.get_sponsor_info(contract_address, w3.cfx.epoch_number_by_tag("latest_state")) # assert sponsor_info TypeValidator.validate_typed_dict(sponsor_info, "SponsorInfo") - + + @pytest.mark.xdist_group(name="account") def test_get_account(self, w3: Web3, address: Base32Address): account_info = w3.cfx.get_account(address, w3.cfx.epoch_number_by_tag("latest_state")) TypeValidator.validate_typed_dict(account_info, "AccountInfo") + @pytest.mark.xdist_group(name="account") def test_get_deposit_list(self, w3:Web3, address: Base32Address): deposit_list = w3.cfx.get_deposit_list(address) for deposit_info in deposit_list: TypeValidator.validate_typed_dict(deposit_info, "DepositInfo") + @pytest.mark.xdist_group(name="account") def test_get_vote_list(self, w3:Web3, address: Base32Address): vote_list = w3.cfx.get_vote_list(address, w3.cfx.epoch_number_by_tag("latest_state")) for vote_info in vote_list: diff --git a/tests/rpcs/cfx/test_cfx_block_rpcs.py b/tests/rpcs/cfx/test_cfx_block_rpcs.py index 54ae9d9..d1f734a 100644 --- a/tests/rpcs/cfx/test_cfx_block_rpcs.py +++ b/tests/rpcs/cfx/test_cfx_block_rpcs.py @@ -33,36 +33,42 @@ def no_full_block_data(self, w3:Web3, block_hash: bytes, use_testnet: bool): block_data = preprocess_block_data(block_data, use_testnet) return block_data + @pytest.mark.xdist_group(name="account") def test_get_block_by_hash(self, block_data: BlockData, no_full_block_data: BlockData): TypeValidator.validate_typed_dict(block_data, "BlockData") TypeValidator.validate_typed_dict(no_full_block_data, "BlockData") + @pytest.mark.xdist_group(name="account") def test_get_block_by_epoch_number(self, w3:Web3, block_data: BlockData, use_testnet: bool): assert block_data['epochNumber'] is not None data_ = w3.cfx.get_block_by_epoch_number(block_data['epochNumber'], True) data_ = preprocess_block_data(data_, use_testnet) TypeValidator.validate_typed_dict(data_, "BlockData") - + + @pytest.mark.xdist_group(name="account") def test_get_block_by_block_number(self, w3:Web3, block_data: BlockData, use_testnet: bool): assert block_data['blockNumber'] is not None data_ = w3.cfx.get_block_by_block_number(block_data['blockNumber'], True) data_ = preprocess_block_data(data_, use_testnet) assert dict(data_) == dict(block_data) + @pytest.mark.xdist_group(name="account") def test_get_best_block_hash(self, w3:Web3): best_block_hash = w3.cfx.get_best_block_hash() assert isinstance(best_block_hash, HexBytes) + @pytest.mark.xdist_group(name="account") def test_get_blocks_by_epoch(self, w3: Web3): blocks = w3.cfx.get_blocks_by_epoch("latest_state") for block_hash in blocks: assert isinstance(block_hash, bytes) + @pytest.mark.xdist_group(name="account") def test_get_skipped_blocks(self, w3: Web3): blocks = w3.cfx.get_skipped_blocks_by_epoch("latest_state") for block_hash in blocks: assert isinstance(block_hash, bytes) - + def test_get_blocks_by_hash_with_pivot_assumptions(self, w3: Web3, use_testnet: bool): epoch_number = w3.cfx.epoch_number_by_tag("latest_confirmed") blocks = w3.cfx.get_blocks_by_epoch(epoch_number) @@ -73,7 +79,8 @@ def test_get_blocks_by_hash_with_pivot_assumptions(self, w3: Web3, use_testnet: ) block_data = preprocess_block_data(block_data, use_testnet) TypeValidator.validate_typed_dict(block_data, "BlockData") - + + @pytest.mark.xdist_group(name="account") def test_get_block(self, w3: Web3, block_data: BlockData, use_testnet: bool): epoch_number = block_data["epochNumber"] assert epoch_number is not None @@ -84,10 +91,11 @@ def test_get_block(self, w3: Web3, block_data: BlockData, use_testnet: bool): block = preprocess_block_data(block, use_testnet) TypeValidator.validate_typed_dict(block, "BlockData") + @pytest.mark.xdist_group(name="account") def test_epoch_receipts(self, w3: Web3, block_data: BlockData): epoch_number = block_data["epochNumber"] assert epoch_number is not None epoch_receipts = w3.cfx.get_epoch_receipts(epoch_number, True) for block_receipts in epoch_receipts: for tx_receipt in block_receipts: - TypeValidator.validate_typed_dict(tx_receipt, "TxReceiptWithSpace") \ No newline at end of file + TypeValidator.validate_typed_dict(tx_receipt, "TxReceiptWithSpace") diff --git a/tests/rpcs/cfx/test_cfx_rpcs.py b/tests/rpcs/cfx/test_cfx_rpcs.py index 2d594a0..5d6f6d3 100644 --- a/tests/rpcs/cfx/test_cfx_rpcs.py +++ b/tests/rpcs/cfx/test_cfx_rpcs.py @@ -1,3 +1,4 @@ +import pytest from hexbytes import HexBytes from cfx_address import Base32Address @@ -11,7 +12,7 @@ # Note that we only test if SDK works as expected, especially for request and result formatting. # We don't test if RPC works as expected - +@pytest.mark.xdist_group(name="account") def test_get_tx(moduled_w3: Web3, contract_address: Base32Address): """test get_transaction(_by_hash) and get_transaction_receipt """ @@ -37,22 +38,25 @@ def test_accounts(w3: Web3, use_testnet: bool): local_node_accounts = w3.cfx.accounts assert len(local_node_accounts) == 10 -def test_get_logs(w3: Web3): - """see test_contract - """ - pass +# def test_get_logs(w3: Web3): +# """see test_contract +# """ +# pass +@pytest.mark.xdist_group(name="account") def test_get_confirmation_risk(w3: Web3, tx_hash: HexBytes): blockHash = w3.cfx.wait_for_transaction_receipt(tx_hash)['blockHash'] risk = w3.cfx.get_confirmation_risk_by_hash(blockHash) assert risk < 1 +@pytest.mark.xdist_group(name="account") def test_fee_history(moduled_w3: Web3): w3 = moduled_w3 fee_history = w3.cfx.fee_history(5, "latest_state", [20,50]) TypeValidator.validate_typed_dict(fee_history, "FeeHistory") +@pytest.mark.xdist_group(name="account") def test_max_priority_fee(moduled_w3: Web3): w3 = moduled_w3 fee = w3.cfx.max_priority_fee diff --git a/tests/rpcs/test_pending_rpcs.py b/tests/rpcs/test_pending_rpcs.py index 3ec7077..d800ef1 100644 --- a/tests/rpcs/test_pending_rpcs.py +++ b/tests/rpcs/test_pending_rpcs.py @@ -1,5 +1,5 @@ import pytest - +from typing import Any from conflux_web3 import Web3 from conflux_web3.contract.metadata import get_contract_metadata @@ -22,12 +22,13 @@ def future_tx(self, moduled_w3: Web3): }) hash.executed() - - def test_get_account_pending_info(self, w3: Web3, address, future_tx): + @pytest.mark.xdist_group(name="account") + def test_get_account_pending_info(self, w3: Web3, address: str, future_tx: Any): account_pending_info = w3.cfx.get_account_pending_info(address) TypeValidator.validate_typed_dict(account_pending_info, "PendingInfo") - def test_get_account_pending_transactions(self, w3: Web3, address, future_tx): + @pytest.mark.xdist_group(name="account") + def test_get_account_pending_transactions(self, w3: Web3, address:str , future_tx: Any): nonce = w3.cfx.get_next_nonce(address) info = w3.cfx.get_account_pending_transactions(address, nonce, 1) assert info["firstTxStatus"] == {"pending": "futureNonce"} diff --git a/tests/rpcs/test_txpool_rpcs.py b/tests/rpcs/test_txpool_rpcs.py index 033dd4a..bb24e5e 100644 --- a/tests/rpcs/test_txpool_rpcs.py +++ b/tests/rpcs/test_txpool_rpcs.py @@ -1,5 +1,5 @@ from conflux_web3 import Web3 -def test_next_nonce(w3: Web3, address): +def test_next_nonce(w3: Web3, address: str): nonce = w3.txpool.next_nonce(address) assert isinstance(nonce, int) diff --git a/tests/transaction/contract/test_embed.py b/tests/transaction/contract/test_embed.py new file mode 100644 index 0000000..e4e8d8a --- /dev/null +++ b/tests/transaction/contract/test_embed.py @@ -0,0 +1,55 @@ +from typing import TYPE_CHECKING + + +from conflux_web3 import Web3 +from conflux_web3.contract.metadata import get_contract_metadata + +if TYPE_CHECKING: + from conflux_web3 import Web3 + + +class TestEmbeddedContractMetadata: + def test_get_contract_metadata(self): + admin_contract_metadata = get_contract_metadata("AdminControl") + assert isinstance(admin_contract_metadata["abi"], list) + assert isinstance(admin_contract_metadata, dict) + + def test_contract_from_metadata(self, w3: Web3, use_testnet: bool): + admin_contract = w3.cfx.contract(**get_contract_metadata("AdminControl")) + assert admin_contract.abi + assert w3.cfx.address.is_valid_base32(admin_contract.address) + + if use_testnet: + + usdt_contract = w3.cfx.contract( + **get_contract_metadata("cUSDT", w3.cfx.chain_id) + ) + assert usdt_contract.abi + assert usdt_contract.bytecode + assert w3.cfx.address.is_valid_base32(usdt_contract.address) + assert usdt_contract.caller.symbol() == "cUSDT" + + def test_contract_from_name(self, w3: Web3, use_testnet: bool): + admin_contract = w3.cfx.contract(name="AdminControl") + assert admin_contract.abi + assert w3.cfx.address.is_valid_base32(admin_contract.address) + + if use_testnet: + usdt_contract = w3.cfx.contract(name="cUSDT") + assert usdt_contract.abi + assert usdt_contract.bytecode + assert w3.cfx.address.is_valid_base32(usdt_contract.address) + assert usdt_contract.caller.symbol() == "cUSDT" + + if w3.cfx.chain_id == 1: + faucet = w3.cfx.contract(name="Faucet") + assert faucet + + # TODO: test all embedded metadata functionalities + def test_faucet_functions(self, w3: Web3): + if w3.cfx.chain_id == 1: + random_account = w3.account.create() + w3.cfx.default_account = random_account + faucet = w3.cfx.contract(name="Faucet") + faucet.functions.claimCfx().transact().executed() + assert w3.cfx.get_balance(w3.cfx.default_account) > 0 diff --git a/tests/transaction/contract/test_erc20.py b/tests/transaction/contract/test_erc20.py new file mode 100644 index 0000000..eb7dad2 --- /dev/null +++ b/tests/transaction/contract/test_erc20.py @@ -0,0 +1,137 @@ +from typing import TYPE_CHECKING +import pytest + + +from conflux_web3 import Web3 +from conflux_web3.contract import ( + ConfluxContract, +) +from conflux_web3.contract.metadata import get_contract_metadata + +from cfx_account import LocalAccount + +from conflux_web3.middleware.wallet import Wallet +from tests._test_helpers.type_check import TypeValidator + +from conflux_web3.utils.address import get_create_address + +if TYPE_CHECKING: + from conflux_web3 import Web3 + + +class TestERC20Contract: + contract: ConfluxContract + + @pytest.fixture + def w3_(self, w3: Web3, account): + """w3 with wallet""" + w3.cfx.default_account = account + w3.middleware_onion.add(Wallet(account)) + return w3 + + # warnings might be raised by web3.py, we just ignore these warnings + @pytest.mark.xdist_group(name="account") + def test_contract_deploy_and_transfer(self, w3_: Web3): + # test deployment + erc20_metadata = get_contract_metadata("ERC20") + erc20 = w3_.cfx.contract( + bytecode=erc20_metadata["bytecode"], abi=erc20_metadata["abi"] + ) + tx_hash = erc20.constructor( + name="Coin", symbol="C", initialSupply=10**18 + ).transact() + contract_address = tx_hash.executed()["contractCreated"] + tx_data = w3_.cfx.get_transaction(tx_hash) + computed_contract_address = get_create_address(w3_.cfx.default_account, tx_data["nonce"], w3_.keccak(tx_data["data"])) # type: ignore + assert contract_address == computed_contract_address, ( + contract_address, + computed_contract_address, + ) + + contract = w3_.cfx.contract(contract_address, abi=erc20_metadata["abi"]) + + # test transfer + random_account = w3_.account.create() + hash = contract.functions.transfer(random_account.address, 100).transact() + transfer_receipt = w3_.cfx.wait_for_transaction_receipt(hash) + balance = contract.functions.balanceOf(random_account.address).call() + assert balance == 100 + + # test contract caller + balance1 = contract.caller().balanceOf(random_account.address) + assert balance1 == 100 + + # test getLogs + from_epoch = transfer_receipt["epochNumber"] + logs = w3_.cfx.get_logs(fromEpoch=from_epoch, address=contract_address) + for log in logs: + TypeValidator.validate_typed_dict(log, "LogReceipt") + + # test contract event + processed_log = contract.events.Transfer.process_receipt(transfer_receipt)[0] + assert processed_log["args"]["from"] == w3_.cfx.default_account, processed_log + assert processed_log["args"]["to"] == random_account.address, processed_log + assert processed_log["args"]["value"] == 100, processed_log + assert processed_log["blockHash"] == logs[0]["blockHash"], processed_log + assert processed_log["epochNumber"] == logs[0]["epochNumber"], processed_log + assert ( + processed_log["transactionHash"] == logs[0]["transactionHash"] + ), processed_log + assert ( + processed_log["transactionLogIndex"] == logs[0]["transactionLogIndex"] + ), processed_log + assert ( + processed_log["transactionIndex"] == logs[0]["transactionIndex"] + ), processed_log + + # test event filters + filter_topics = contract.events.Transfer.get_filter_topics( + value=100, to=random_account.address + ) + assert filter_topics + new_logs = w3_.cfx.get_logs(fromEpoch=from_epoch, topics=filter_topics) + assert new_logs == logs + + # test event get_logs + new_processed_logs = contract.events.Transfer.get_logs( + argument_filters={"value": 100, "to": random_account.address}, + from_epoch=from_epoch, + ) + assert new_processed_logs[0]["args"] == processed_log["args"] + + @pytest.mark.xdist_group(name="account") + def test_contract_without_wallet(self, w3: Web3, account: LocalAccount): + erc20_metadata = get_contract_metadata("ERC20") + + erc20 = w3.cfx.contract( + bytecode=erc20_metadata["bytecode"], abi=erc20_metadata["abi"] + ) + # test raw + prebuilt_tx_params = erc20.constructor( + name="Coin", symbol="C", initialSupply=10**18 + ).build_transaction( + { + "from": account.address, + # 'nonce': w3.cfx.get_next_nonce(account.address), + # 'value': 0, + # 'gas': 21000, + # 'gasPrice': 10**9, + # 'chainId': w3.cfx.chain_id, + # 'epochHeight': w3.cfx.epoch_number + } + ) + + raw_constuct_tx = account.sign_transaction(prebuilt_tx_params).raw_transaction + contract_address = w3.cfx.send_raw_transaction(raw_constuct_tx).executed()[ + "contractCreated" + ] + assert contract_address + + contract_instance = w3.cfx.contract( + address=contract_address, abi=erc20_metadata["abi"] + ) + prebuilt_transfer = contract_instance.functions.transfer( + w3.account.create().address, 100 + ).build_transaction({"from": account.address}) + raw_tx = account.sign_transaction(prebuilt_transfer).raw_transaction + w3.cfx.send_raw_transaction(raw_tx).executed() diff --git a/tests/transaction/contract/test_others.py b/tests/transaction/contract/test_others.py new file mode 100644 index 0000000..bd30f0f --- /dev/null +++ b/tests/transaction/contract/test_others.py @@ -0,0 +1,44 @@ +from typing import TYPE_CHECKING +import pytest + + +from conflux_web3 import Web3 +from conflux_web3.contract.metadata import get_contract_metadata +from cfx_utils.exceptions import Base32AddressNotMatch + + +if TYPE_CHECKING: + from conflux_web3 import Web3 + + +def test_contract_initialization(w3: Web3): + metadata = get_contract_metadata("AdminControl") + chain_id = w3.cfx.chain_id + metadata["address"] = w3.cfx.address(metadata["address"], chain_id + 1) + + with pytest.raises(Base32AddressNotMatch): + w3.cfx.contract(**metadata) + + +def test_contract_with_no_deployment_info(w3: Web3): + c = w3.cfx.contract(name="AdminControl", with_deployment_info=False) + assert not c.address + + +# This is an error from upstream web3.py and will be fixed in https://github.com/ethereum/web3.py/issues/3482 +# def test_get_function_by_signature(w3: Web3, account: LocalAccount): +# from tests._test_helpers.ENV_SETTING import HELPER_DIR +# with open(os.path.join(HELPER_DIR, "amb_metadata.json")) as f: +# metadata = json.load(f) +# contract = w3.cfx.contract(abi=metadata['abi'], bytecode=metadata["bin"]) +# # with pytest.raises(ValidationError): +# # contract.functions.identity(account.address, True) + +# w3.cfx.default_account = account +# contract_addr = contract.constructor().transact().executed()["contractCreated"] +# assert contract_addr +# contract = contract(contract_addr) +# func = contract.get_function_by_signature('identity(address,bool)') +# assert func(account.address, True).call() == account.address # returned address should be in Base32 format +# assert contract.get_function_by_signature('identity(string,bool)')(account.address, True).call() == account.address +# assert contract.get_function_by_signature('identity(string,bool)')("conflux", True).call() == "conflux" diff --git a/tests/transaction/test_base_tx.py b/tests/transaction/test_base_tx.py index 10f4137..f40f008 100644 --- a/tests/transaction/test_base_tx.py +++ b/tests/transaction/test_base_tx.py @@ -1,9 +1,11 @@ +import pytest from typing import Sequence from cfx_account.account import LocalAccount from conflux_web3 import Web3 from conflux_web3.types import Base32Address from tests._test_helpers.type_check import TypeValidator +@pytest.mark.xdist_group(name="account") def test_send_raw_transaction(w3: Web3, account: LocalAccount): status = w3.cfx.get_status() diff --git a/tests/transaction/test_contract.py b/tests/transaction/test_contract.py deleted file mode 100644 index b771a77..0000000 --- a/tests/transaction/test_contract.py +++ /dev/null @@ -1,199 +0,0 @@ -from typing import TYPE_CHECKING -import pytest - - -from conflux_web3 import Web3 -from conflux_web3.contract import ( - ConfluxContract, -) -from conflux_web3.contract.metadata import ( - get_contract_metadata -) -from cfx_utils.exceptions import Base32AddressNotMatch - -from cfx_account import LocalAccount - -from conflux_web3.middleware.wallet import Wallet -from tests._test_helpers.type_check import TypeValidator - -from conflux_web3.utils.address import get_create_address - -if TYPE_CHECKING: - from conflux_web3 import Web3 - -class TestERC20Contract: - contract: ConfluxContract - - @pytest.fixture - def w3_(self, w3:Web3, account): - """w3 with wallet - """ - w3.cfx.default_account = account - w3.middleware_onion.add( - Wallet(account) - ) - return w3 - - # warnings might be raised by web3.py, we just ignore these warnings - def test_contract_deploy_and_transfer(self, w3_: Web3): - # test deployment - erc20_metadata = get_contract_metadata("ERC20") - erc20 = w3_.cfx.contract(bytecode=erc20_metadata["bytecode"], abi=erc20_metadata["abi"]) - tx_hash = erc20.constructor(name="Coin", symbol="C", initialSupply=10**18).transact() - contract_address = tx_hash.executed()["contractCreated"] - tx_data = w3_.cfx.get_transaction(tx_hash) - computed_contract_address = get_create_address(w3_.cfx.default_account, tx_data["nonce"], w3_.keccak(tx_data["data"])) # type: ignore - assert contract_address == computed_contract_address, (contract_address, computed_contract_address) - - contract = w3_.cfx.contract(contract_address, abi=erc20_metadata["abi"]) - - # test transfer - random_account = w3_.account.create() - hash = contract.functions.transfer(random_account.address, 100).transact() - transfer_receipt = w3_.cfx.wait_for_transaction_receipt(hash) - balance = contract.functions.balanceOf(random_account.address).call() - assert balance == 100 - - # test contract caller - balance1 = contract.caller().balanceOf(random_account.address) - assert balance1 == 100 - - # test getLogs - from_epoch = transfer_receipt["epochNumber"] - logs = w3_.cfx.get_logs(fromEpoch=from_epoch, address=contract_address) - for log in logs: - TypeValidator.validate_typed_dict(log, "LogReceipt") - - # test contract event - processed_log = contract.events.Transfer.process_receipt(transfer_receipt)[0] - assert processed_log["args"]["from"] == w3_.cfx.default_account, processed_log - assert processed_log["args"]["to"] == random_account.address, processed_log - assert processed_log["args"]["value"] == 100, processed_log - assert processed_log["blockHash"] == logs[0]["blockHash"], processed_log - assert processed_log["epochNumber"] == logs[0]["epochNumber"], processed_log - assert processed_log["transactionHash"] == logs[0]["transactionHash"], processed_log - assert processed_log["transactionLogIndex"] == logs[0]["transactionLogIndex"], processed_log - assert processed_log["transactionIndex"] == logs[0]["transactionIndex"], processed_log - - # test event filters - filter_topics = contract.events.Transfer.get_filter_topics( - value=100, - to=random_account.address - ) - assert filter_topics - new_logs = w3_.cfx.get_logs(fromEpoch=from_epoch, topics=filter_topics) - assert new_logs == logs - - # test event get_logs - new_processed_logs = contract.events.Transfer.get_logs( - argument_filters={ - "value": 100, - "to": random_account.address - }, - from_epoch=from_epoch - ) - assert new_processed_logs[0]["args"] == processed_log["args"] - - def test_contract_without_wallet(self, w3: Web3, account: LocalAccount): - erc20_metadata = get_contract_metadata("ERC20") - - erc20 = w3.cfx.contract(bytecode=erc20_metadata["bytecode"], abi=erc20_metadata["abi"]) - # test raw - prebuilt_tx_params = erc20.constructor(name="Coin", symbol="C", initialSupply=10**18).build_transaction({ - 'from': account.address, - # 'nonce': w3.cfx.get_next_nonce(account.address), - # 'value': 0, - # 'gas': 21000, - # 'gasPrice': 10**9, - # 'chainId': w3.cfx.chain_id, - # 'epochHeight': w3.cfx.epoch_number - }) - - raw_constuct_tx = account.sign_transaction(prebuilt_tx_params).raw_transaction - contract_address = w3.cfx.send_raw_transaction(raw_constuct_tx).executed()["contractCreated"] - assert contract_address - - contract_instance = w3.cfx.contract(address=contract_address, abi=erc20_metadata["abi"]) - prebuilt_transfer = contract_instance.functions.transfer( - w3.account.create().address, - 100 - ).build_transaction({ - 'from': account.address - }) - raw_tx = account.sign_transaction(prebuilt_transfer).raw_transaction - w3.cfx.send_raw_transaction(raw_tx).executed() - -class TestEmbeddedContractMetadata: - def test_get_contract_metadata(self): - admin_contract_metadata = get_contract_metadata("AdminControl") - assert isinstance(admin_contract_metadata["abi"], list) - assert isinstance(admin_contract_metadata, dict) - - def test_contract_from_metadata(self, w3: Web3, use_testnet: bool): - admin_contract = w3.cfx.contract(**get_contract_metadata("AdminControl")) - assert admin_contract.abi - assert w3.cfx.address.is_valid_base32(admin_contract.address) - - if use_testnet: - - usdt_contract = w3.cfx.contract(**get_contract_metadata("cUSDT", w3.cfx.chain_id)) - assert usdt_contract.abi - assert usdt_contract.bytecode - assert w3.cfx.address.is_valid_base32(usdt_contract.address) - assert usdt_contract.caller.symbol() == "cUSDT" - - def test_contract_from_name(self, w3: Web3, use_testnet: bool): - admin_contract = w3.cfx.contract(name="AdminControl") - assert admin_contract.abi - assert w3.cfx.address.is_valid_base32(admin_contract.address) - - if use_testnet: - usdt_contract = w3.cfx.contract(name="cUSDT") - assert usdt_contract.abi - assert usdt_contract.bytecode - assert w3.cfx.address.is_valid_base32(usdt_contract.address) - assert usdt_contract.caller.symbol() == "cUSDT" - - if w3.cfx.chain_id == 1: - faucet = w3.cfx.contract(name="Faucet") - assert faucet - - # TODO: test all embedded metadata functionalities - def test_faucet_functions(self, w3: Web3): - if w3.cfx.chain_id == 1: - random_account = w3.account.create() - w3.cfx.default_account = random_account - faucet = w3.cfx.contract(name="Faucet") - faucet.functions.claimCfx().transact().executed() - assert w3.cfx.get_balance(w3.cfx.default_account) > 0 - - -def test_contract_initialization(w3: Web3): - metadata = get_contract_metadata("AdminControl") - chain_id = w3.cfx.chain_id - metadata["address"] = w3.cfx.address(metadata["address"], chain_id+1) - - with pytest.raises(Base32AddressNotMatch): - w3.cfx.contract(**metadata) - -def test_contract_with_no_deployment_info(w3: Web3): - c = w3.cfx.contract(name="AdminControl", with_deployment_info=False) - assert not c.address - -# This is an error from upstream web3.py and will be fixed in https://github.com/ethereum/web3.py/issues/3482 -# def test_get_function_by_signature(w3: Web3, account: LocalAccount): -# from tests._test_helpers.ENV_SETTING import HELPER_DIR -# with open(os.path.join(HELPER_DIR, "amb_metadata.json")) as f: -# metadata = json.load(f) -# contract = w3.cfx.contract(abi=metadata['abi'], bytecode=metadata["bin"]) -# # with pytest.raises(ValidationError): -# # contract.functions.identity(account.address, True) - -# w3.cfx.default_account = account -# contract_addr = contract.constructor().transact().executed()["contractCreated"] -# assert contract_addr -# contract = contract(contract_addr) -# func = contract.get_function_by_signature('identity(address,bool)') -# assert func(account.address, True).call() == account.address # returned address should be in Base32 format -# assert contract.get_function_by_signature('identity(string,bool)')(account.address, True).call() == account.address -# assert contract.get_function_by_signature('identity(string,bool)')("conflux", True).call() == "conflux"