Skip to content

Commit 71d46fd

Browse files
committed
test: use new Factory feature, re-arrange integration & func tests
1 parent 240d2d9 commit 71d46fd

File tree

5 files changed

+76
-144
lines changed

5 files changed

+76
-144
lines changed

tests/conftest.py

Lines changed: 19 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,29 @@
11
import json
2-
import shutil
32
import tempfile
43
from pathlib import Path
54

6-
import ape
75
import pytest
86
from ape.contracts import ContractContainer
9-
from ape.utils import ZERO_ADDRESS, create_tempdir
107
from ethpm_types import ContractType
8+
from packaging.version import Version
119

1210
from ape_safe import MultiSend
1311
from ape_safe.accounts import SafeAccount
12+
from ape_safe.factory import Factory
1413

1514
contracts_directory = Path(__file__).parent / "contracts"
16-
TESTS_DIR = Path(__file__).parent.absolute()
1715

18-
# TODO: Test more versions.
19-
VERSIONS = ("1.3.0", "1.1.1")
2016

21-
22-
@pytest.fixture(scope="session", autouse=True)
23-
def config(project):
24-
cfg = ape.config
25-
26-
# Ensure we have the safe-contracts dependencies.
27-
for version in VERSIONS:
28-
_ = project.dependencies.get_dependency("safe-contracts", version)
29-
30-
# Ensure we don't persist any .ape data.
31-
with create_tempdir() as path:
32-
# First, copy in Safe contracts so we don't download each time.
33-
src = cfg.DATA_FOLDER / "packages"
34-
dest = path / "packages"
35-
# NOTE: Only copy specific safe-global contracts (for local dev)
36-
for subfolder in src.glob("*/safe-global*"):
37-
shutil.copytree(subfolder, dest / subfolder.parent.name / subfolder.name)
38-
cfg.DATA_FOLDER = path
39-
yield cfg
40-
41-
42-
@pytest.fixture
43-
def data_folder(config):
44-
return config.DATA_FOLDER / "safe"
17+
@pytest.fixture(
18+
scope="session",
19+
# TODO: Test more versions.
20+
params=(
21+
"1.3.0",
22+
"1.1.1",
23+
),
24+
)
25+
def VERSION(request):
26+
return Version(request.param)
4527

4628

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

5133

5234
@pytest.fixture(scope="session")
53-
def receiver(accounts):
54-
return accounts[9]
55-
56-
57-
@pytest.fixture(scope="session", params=VERSIONS)
58-
def VERSION(request):
59-
return request.param
60-
61-
62-
@pytest.fixture(scope="session")
63-
def safe_contracts(project, VERSION):
64-
dependency = project.dependencies.get_dependency("safe-contracts", VERSION)
65-
return dependency.project
35+
def safe_factory(VERSION, deployer):
36+
Factory.inject(VERSION, deployer)
37+
return Factory()
6638

6739

6840
@pytest.fixture(scope="session")
69-
def SafeSingleton(safe_contracts):
70-
return safe_contracts.GnosisSafe
71-
72-
73-
@pytest.fixture
74-
def singleton(deployer: SafeAccount, SafeSingleton):
75-
return deployer.deploy(SafeSingleton)
76-
77-
78-
@pytest.fixture(scope="session")
79-
def SafeProxy(safe_contracts, SafeSingleton):
80-
Proxy = safe_contracts.GnosisSafeProxy
81-
IProxy = safe_contracts.IProxy
82-
# NOTE: Proxy only has a constructor, so we add the rest of it's ABI here for simplified use
83-
Proxy.contract_type.abi += [IProxy.contract_type.abi[0], *SafeSingleton.contract_type.abi]
84-
return Proxy
41+
def receiver(accounts):
42+
return accounts[9]
8543

8644

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

10462

10563
@pytest.fixture
106-
def safe_contract(singleton, SafeProxy, OWNERS, THRESHOLD):
107-
deployer = OWNERS[0]
108-
safe = deployer.deploy(SafeProxy, singleton)
109-
safe.setup(
110-
OWNERS,
111-
THRESHOLD,
112-
# no modules
113-
ZERO_ADDRESS,
114-
b"",
115-
# no fallback
116-
ZERO_ADDRESS,
117-
# no payment
118-
ZERO_ADDRESS,
119-
0,
120-
ZERO_ADDRESS,
121-
sender=deployer,
122-
)
123-
return safe
64+
def safe_contract(safe_factory, deployer, OWNERS, THRESHOLD):
65+
return safe_factory.create(OWNERS, THRESHOLD, sender=deployer)
12466

12567

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

14587

146-
@pytest.fixture
147-
def safes():
148-
return ape.accounts.containers["safe"]
149-
150-
15188
@pytest.fixture
15289
def token(deployer: SafeAccount):
15390
text = (contracts_directory / "Token.json").read_text()

tests/functional/test_account.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
1-
from pathlib import Path
2-
31
import pytest
42
from ape.api import AccountAPI
53
from ape.exceptions import SignatureError
64
from ape.types import AddressType
75
from eth_utils import add_0x_prefix
86

97

10-
def test_data_folder(safes, config):
11-
assert Path.home() not in safes.data_folder.parents
12-
assert safes.data_folder == config.DATA_FOLDER / "safe"
13-
14-
158
def test_init(safe, OWNERS, THRESHOLD, safe_contract):
169
assert safe.contract == safe_contract
1710
assert safe.confirmations_required == THRESHOLD

tests/integration/conftest.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,41 @@
1-
import shutil
2-
from contextlib import contextmanager
1+
import tempfile
2+
from pathlib import Path
33

4+
import ape
45
import pytest
5-
from ape.utils import create_tempdir
66
from click.testing import CliRunner
77

8-
from ape_safe._cli import cli as CLI
8+
from ape_safe._cli import cli as ape_safe_cli
99

1010

1111
@pytest.fixture
12-
def runner():
13-
return CliRunner()
12+
def safe_container():
13+
return ape.accounts.containers["safe"]
14+
15+
16+
# NOTE: Every test gets a different data folder
17+
@pytest.fixture(scope="function", autouse=True)
18+
def patch_data_folder(monkeypatch, safe_container):
19+
with tempfile.TemporaryDirectory() as data_folder_override:
20+
monkeypatch.setattr(safe_container, "data_folder", Path(data_folder_override))
21+
yield
1422

1523

1624
@pytest.fixture
17-
def cli():
18-
return CLI
25+
def runner():
26+
yield CliRunner(mix_stderr=True)
1927

2028

2129
@pytest.fixture
22-
def no_safes(data_folder):
23-
with _remove_safes(data_folder):
24-
yield
30+
def cli():
31+
return ape_safe_cli
2532

2633

2734
@pytest.fixture
28-
def one_safe(data_folder, safes, safe):
29-
with _remove_safes(data_folder):
30-
safes.save_account(safe.alias, safe.address)
31-
yield safes.load_account(safe.alias)
35+
def safe_account(safe_container, safe):
36+
safe_container.save_account(safe.alias, safe.address)
3237

38+
yield safe_container.load_account(safe.alias)
3339

34-
@contextmanager
35-
def _remove_safes(data_folder):
36-
with create_tempdir() as temp_dir:
37-
dest = temp_dir / "dest"
38-
shutil.move(data_folder, dest)
39-
yield
40-
shutil.move(dest, data_folder)
40+
if safe.alias in safe_container.aliases:
41+
safe_container.delete_account(safe.alias)

tests/integration/test_pending_cli.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ def test_help(runner, cli):
88
assert result.exit_code == 0, result.output
99

1010

11-
def test_propose(runner, cli, one_safe, receiver, chain):
12-
nonce_at_start = one_safe.next_nonce
11+
def test_propose(runner, cli, safe_account, receiver, chain):
12+
nonce_at_start = safe_account.next_nonce
1313
cmd = (
1414
"pending",
1515
"propose",
@@ -23,25 +23,25 @@ def test_propose(runner, cli, one_safe, receiver, chain):
2323

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

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

3333
# Verify the transaction is in the service.
34-
assert safe_tx_hash in one_safe.client.transactions
34+
assert safe_tx_hash in safe_account.client.transactions
3535

3636
# The nonce is the same because we did not execute.
37-
assert one_safe.next_nonce == nonce_at_start
37+
assert safe_account.next_nonce == nonce_at_start
3838

3939

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

44-
nonce_at_start = one_safe.next_nonce
44+
nonce_at_start = safe_account.next_nonce
4545
cmd = (
4646
"pending",
4747
"propose",
@@ -58,14 +58,14 @@ def test_propose_with_sender(runner, cli, one_safe, receiver, chain, foundry):
5858
assert result.exit_code == 0, result.output
5959

6060
# The nonce is the same because we did not execute.
61-
assert one_safe.next_nonce == nonce_at_start
61+
assert safe_account.next_nonce == nonce_at_start
6262

6363

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

68-
nonce_at_start = one_safe.next_nonce
68+
nonce_at_start = safe_account.next_nonce
6969
cmd = (
7070
"pending",
7171
"propose",
@@ -81,24 +81,24 @@ def test_propose_with_execute(runner, cli, one_safe, receiver, chain):
8181
)
8282
result = runner.invoke(cli, cmd, catch_exceptions=False)
8383
assert result.exit_code == 0, result.output
84-
assert one_safe.next_nonce == nonce_at_start + 1
84+
assert safe_account.next_nonce == nonce_at_start + 1
8585

8686

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

9393

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

100100

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

112112

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

119-
one_safe.client.transactions_by_nonce[nonce] = tx_hash
120-
one_safe.client.transactions[tx_hash] = ExecutedTxData(
119+
safe_account.client.transactions_by_nonce[nonce] = tx_hash
120+
safe_account.client.transactions[tx_hash] = ExecutedTxData(
121121
executionDate=datetime.now(),
122122
blockNumber=0,
123123
transactionHash=tx_hash,
@@ -144,7 +144,7 @@ def test_approve(receiver, runner, cli, one_safe, chain):
144144
operation=0,
145145
value=0,
146146
to=receiver.address,
147-
safe=one_safe.address,
147+
safe=safe_account.address,
148148
)
149149

150150
arguments = ("pending", "approve", tx_hash, "--network", chain.provider.network_choice)

0 commit comments

Comments
 (0)