Skip to content

Commit

Permalink
test: use new Factory feature, re-arrange integration & func tests
Browse files Browse the repository at this point in the history
  • Loading branch information
fubuloubu committed Feb 25, 2025
1 parent 240d2d9 commit 71d46fd
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 144 deletions.
101 changes: 19 additions & 82 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,29 @@
import json
import shutil
import tempfile
from pathlib import Path

import ape
import pytest
from ape.contracts import ContractContainer
from ape.utils import ZERO_ADDRESS, create_tempdir
from ethpm_types import ContractType
from packaging.version import Version

from ape_safe import MultiSend
from ape_safe.accounts import SafeAccount
from ape_safe.factory import Factory

contracts_directory = Path(__file__).parent / "contracts"
TESTS_DIR = Path(__file__).parent.absolute()

# TODO: Test more versions.
VERSIONS = ("1.3.0", "1.1.1")


@pytest.fixture(scope="session", autouse=True)
def config(project):
cfg = ape.config

# Ensure we have the safe-contracts dependencies.
for version in VERSIONS:
_ = project.dependencies.get_dependency("safe-contracts", version)

# Ensure we don't persist any .ape data.
with create_tempdir() as path:
# First, copy in Safe contracts so we don't download each time.
src = cfg.DATA_FOLDER / "packages"
dest = path / "packages"
# NOTE: Only copy specific safe-global contracts (for local dev)
for subfolder in src.glob("*/safe-global*"):
shutil.copytree(subfolder, dest / subfolder.parent.name / subfolder.name)
cfg.DATA_FOLDER = path
yield cfg


@pytest.fixture
def data_folder(config):
return config.DATA_FOLDER / "safe"
@pytest.fixture(
scope="session",
# TODO: Test more versions.
params=(
"1.3.0",
"1.1.1",
),
)
def VERSION(request):
return Version(request.param)


@pytest.fixture(scope="session")
Expand All @@ -50,38 +32,14 @@ def deployer(OWNERS):


@pytest.fixture(scope="session")
def receiver(accounts):
return accounts[9]


@pytest.fixture(scope="session", params=VERSIONS)
def VERSION(request):
return request.param


@pytest.fixture(scope="session")
def safe_contracts(project, VERSION):
dependency = project.dependencies.get_dependency("safe-contracts", VERSION)
return dependency.project
def safe_factory(VERSION, deployer):
Factory.inject(VERSION, deployer)
return Factory()


@pytest.fixture(scope="session")
def SafeSingleton(safe_contracts):
return safe_contracts.GnosisSafe


@pytest.fixture
def singleton(deployer: SafeAccount, SafeSingleton):
return deployer.deploy(SafeSingleton)


@pytest.fixture(scope="session")
def SafeProxy(safe_contracts, SafeSingleton):
Proxy = safe_contracts.GnosisSafeProxy
IProxy = safe_contracts.IProxy
# NOTE: Proxy only has a constructor, so we add the rest of it's ABI here for simplified use
Proxy.contract_type.abi += [IProxy.contract_type.abi[0], *SafeSingleton.contract_type.abi]
return Proxy
def receiver(accounts):
return accounts[9]


@pytest.fixture(scope="session", params=["1/1", "1/2", "2/2", "2/3", "3/3"])
Expand All @@ -103,24 +61,8 @@ def OWNERS(accounts, MULTISIG_TYPE):


@pytest.fixture
def safe_contract(singleton, SafeProxy, OWNERS, THRESHOLD):
deployer = OWNERS[0]
safe = deployer.deploy(SafeProxy, singleton)
safe.setup(
OWNERS,
THRESHOLD,
# no modules
ZERO_ADDRESS,
b"",
# no fallback
ZERO_ADDRESS,
# no payment
ZERO_ADDRESS,
0,
ZERO_ADDRESS,
sender=deployer,
)
return safe
def safe_contract(safe_factory, deployer, OWNERS, THRESHOLD):
return safe_factory.create(OWNERS, THRESHOLD, sender=deployer)


@pytest.fixture
Expand All @@ -143,11 +85,6 @@ def safe(safe_data_file):
return SafeAccount(account_file_path=safe_data_file)


@pytest.fixture
def safes():
return ape.accounts.containers["safe"]


@pytest.fixture
def token(deployer: SafeAccount):
text = (contracts_directory / "Token.json").read_text()
Expand Down
7 changes: 0 additions & 7 deletions tests/functional/test_account.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
from pathlib import Path

import pytest
from ape.api import AccountAPI
from ape.exceptions import SignatureError
from ape.types import AddressType
from eth_utils import add_0x_prefix


def test_data_folder(safes, config):
assert Path.home() not in safes.data_folder.parents
assert safes.data_folder == config.DATA_FOLDER / "safe"


def test_init(safe, OWNERS, THRESHOLD, safe_contract):
assert safe.contract == safe_contract
assert safe.confirmations_required == THRESHOLD
Expand Down
45 changes: 23 additions & 22 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
import shutil
from contextlib import contextmanager
import tempfile
from pathlib import Path

import ape
import pytest
from ape.utils import create_tempdir
from click.testing import CliRunner

from ape_safe._cli import cli as CLI
from ape_safe._cli import cli as ape_safe_cli


@pytest.fixture
def runner():
return CliRunner()
def safe_container():
return ape.accounts.containers["safe"]


# NOTE: Every test gets a different data folder
@pytest.fixture(scope="function", autouse=True)
def patch_data_folder(monkeypatch, safe_container):
with tempfile.TemporaryDirectory() as data_folder_override:
monkeypatch.setattr(safe_container, "data_folder", Path(data_folder_override))
yield


@pytest.fixture
def cli():
return CLI
def runner():
yield CliRunner(mix_stderr=True)


@pytest.fixture
def no_safes(data_folder):
with _remove_safes(data_folder):
yield
def cli():
return ape_safe_cli


@pytest.fixture
def one_safe(data_folder, safes, safe):
with _remove_safes(data_folder):
safes.save_account(safe.alias, safe.address)
yield safes.load_account(safe.alias)
def safe_account(safe_container, safe):
safe_container.save_account(safe.alias, safe.address)

yield safe_container.load_account(safe.alias)

@contextmanager
def _remove_safes(data_folder):
with create_tempdir() as temp_dir:
dest = temp_dir / "dest"
shutil.move(data_folder, dest)
yield
shutil.move(dest, data_folder)
if safe.alias in safe_container.aliases:
safe_container.delete_account(safe.alias)
42 changes: 21 additions & 21 deletions tests/integration/test_pending_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ def test_help(runner, cli):
assert result.exit_code == 0, result.output


def test_propose(runner, cli, one_safe, receiver, chain):
nonce_at_start = one_safe.next_nonce
def test_propose(runner, cli, safe_account, receiver, chain):
nonce_at_start = safe_account.next_nonce
cmd = (
"pending",
"propose",
Expand All @@ -23,25 +23,25 @@ def test_propose(runner, cli, one_safe, receiver, chain):

# Sender is required by the API even for initial proposal,
# so it prompts the user.
sender_input = f"{one_safe.alias}\n"
sender_input = f"{safe_account.alias}\n"

result = runner.invoke(cli, cmd, catch_exceptions=False, input=sender_input)
assert result.exit_code == 0
assert "Proposed transaction" in result.output
safe_tx_hash = result.output.split("Proposed transaction '")[-1].split("'")[0].strip()

# Verify the transaction is in the service.
assert safe_tx_hash in one_safe.client.transactions
assert safe_tx_hash in safe_account.client.transactions

# The nonce is the same because we did not execute.
assert one_safe.next_nonce == nonce_at_start
assert safe_account.next_nonce == nonce_at_start


def test_propose_with_sender(runner, cli, one_safe, receiver, chain, foundry):
def test_propose_with_sender(runner, cli, safe_account, receiver, chain, foundry):
# First, fund the safe so the tx does not fail.
receiver.transfer(one_safe, "1 ETH")
receiver.transfer(safe_account, "1 ETH")

nonce_at_start = one_safe.next_nonce
nonce_at_start = safe_account.next_nonce
cmd = (
"pending",
"propose",
Expand All @@ -58,14 +58,14 @@ def test_propose_with_sender(runner, cli, one_safe, receiver, chain, foundry):
assert result.exit_code == 0, result.output

# The nonce is the same because we did not execute.
assert one_safe.next_nonce == nonce_at_start
assert safe_account.next_nonce == nonce_at_start


def test_propose_with_execute(runner, cli, one_safe, receiver, chain):
def test_propose_with_execute(runner, cli, safe_account, receiver, chain):
# First, fund the safe so the tx does not fail.
receiver.transfer(one_safe, "1 ETH")
receiver.transfer(safe_account, "1 ETH")

nonce_at_start = one_safe.next_nonce
nonce_at_start = safe_account.next_nonce
cmd = (
"pending",
"propose",
Expand All @@ -81,24 +81,24 @@ def test_propose_with_execute(runner, cli, one_safe, receiver, chain):
)
result = runner.invoke(cli, cmd, catch_exceptions=False)
assert result.exit_code == 0, result.output
assert one_safe.next_nonce == nonce_at_start + 1
assert safe_account.next_nonce == nonce_at_start + 1


def test_list_no_safes(runner, cli, no_safes, chain):
def test_list_no_safes(runner, cli, chain):
result = runner.invoke(cli, ["pending", "list", "--network", chain.provider.network_choice])
assert result.exit_code != 0, result.output
assert "First, add a safe account using command" in result.output
assert "ape safe add" in result.output


def test_list_no_txns(runner, cli, one_safe, chain):
def test_list_no_txns(runner, cli, safe_account, chain):
arguments = ("pending", "list", "--network", chain.provider.network_choice)
result = runner.invoke(cli, arguments, catch_exceptions=False)
assert result.exit_code == 0, result.output
assert "There are no pending transactions" in result.output


def test_approve_transaction_not_found(runner, cli, one_safe, chain):
def test_approve_transaction_not_found(runner, cli, safe_account, chain):
tx_hash = "0x123"
arguments = ("pending", "approve", tx_hash, "--network", chain.provider.network_choice)
result = runner.invoke(
Expand All @@ -110,14 +110,14 @@ def test_approve_transaction_not_found(runner, cli, one_safe, chain):
assert f"Pending transaction(s) '{tx_hash}' not found." in result.output


def test_approve(receiver, runner, cli, one_safe, chain):
def test_approve(receiver, runner, cli, safe_account, chain):
# First, fund the safe so the tx does not fail.
receiver.transfer(one_safe, "1 ETH")
receiver.transfer(safe_account, "1 ETH")
tx_hash = "0x123"
nonce = 1

one_safe.client.transactions_by_nonce[nonce] = tx_hash
one_safe.client.transactions[tx_hash] = ExecutedTxData(
safe_account.client.transactions_by_nonce[nonce] = tx_hash
safe_account.client.transactions[tx_hash] = ExecutedTxData(
executionDate=datetime.now(),
blockNumber=0,
transactionHash=tx_hash,
Expand All @@ -144,7 +144,7 @@ def test_approve(receiver, runner, cli, one_safe, chain):
operation=0,
value=0,
to=receiver.address,
safe=one_safe.address,
safe=safe_account.address,
)

arguments = ("pending", "approve", tx_hash, "--network", chain.provider.network_choice)
Expand Down
Loading

0 comments on commit 71d46fd

Please sign in to comment.