Skip to content

Commit

Permalink
test: use anvil on functional test
Browse files Browse the repository at this point in the history
  • Loading branch information
pandadefi committed Feb 2, 2023
1 parent db6ebc0 commit 6903c37
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 95 deletions.
87 changes: 53 additions & 34 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,8 @@ concurrency:
cancel-in-progress: true

jobs:

functional:
runs-on: ubuntu-latest
strategy:
matrix:
group: [1, 2, 3, 4, 5, 6]

steps:
- uses: actions/checkout@v1

Expand All @@ -35,8 +30,8 @@ jobs:
with:
node-version: '16.x'

- name: Install ganache
run: npm install -g ganache
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Set up python 3.8
uses: actions/setup-python@v2
Expand All @@ -59,45 +54,69 @@ jobs:
${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
${{ runner.os }}-pip-
- name: Get Month
id: get-month
run: echo "::set-output name=date::$(/bin/date -u "+%Y%m")"
shell: bash
- name: Install python dependencies
run: pip install -r requirements-dev.txt

- name: Compile Code
run: brownie compile --size

- name: Run test functional
id: test_functional
run: brownie test tests/functional --network anvil --ignore=tests/functional/registry/

functional-registry:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1

- name: Restore duration cache
- name: Cache compiler installations
uses: actions/cache@v2
id: test_durations_cache
with:
path: .test_durations
key: ${{ runner.os }}-${{ steps.get-month.outputs.date }}-test-durations-cache-

- name: Check file existence
id: check_test_durations
uses: andstor/file-existence-action@v1
path: |
~/.solcx
~/.vvm/vyper-*
key: ${{ runner.os }}-compiler-cache

- name: Setup node.js
uses: actions/setup-node@v1
with:
node-version: '16.x'

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Set up python 3.8
uses: actions/setup-python@v2
with:
files: .test_durations
python-version: 3.8

- name: Set pip cache directory path
id: pip-cache-dir-path
run: |
echo "::set-output name=dir::$(pip cache dir)"
- name: cat
if: steps.check_test_durations.outputs.files_exists == 'true'
run: cat .test_durations
- name: Restore pip cache
uses: actions/cache@v2
id: pip-cache
with:
path: |
${{ steps.pip-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
${{ runner.os }}-pip-
- name: Install python dependencies
run: pip install -r requirements-dev.txt

- name: Install pytest-split
run: pip install pytest-split

- name: Compile Code
run: brownie compile --size
- name: start anvil
run: anvil --port 8545 & # test hang if started from brownie

- name: Run Splitted Tests
if: steps.check_test_durations.outputs.files_exists == 'true'
run: brownie test tests/functional --gas --coverage --splits 6 --group ${{ matrix.group }};

- name: Run build test_duration
if: steps.check_test_durations.outputs.files_exists == 'false' # has to run on all, otherwise the first one that finishes creates an empty cache and lock the cache for others
id: build_cache_duration
run: brownie test tests/functional --store-durations --gas --coverage
- name: Run test functional registry
id: test_functional_registry
run: brownie test tests/functional/registry/ --network anvil

integration:
runs-on: ubuntu-latest
Expand Down
34 changes: 33 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@
from eth_account import Account
from eth_account.messages import encode_structured_data

from brownie import compile_source, Token, Vault, web3, chain
from brownie import compile_source, Token, Vault, web3, chain, network


@pytest.fixture(scope="function", autouse=True)
def max_fee(web3):
if chain.id == 31337:
network.max_fee("5 gwei")
network.priority_fee("1 gwei")


PACKAGE_VERSION = yaml.safe_load(
(Path(__file__).parents[1] / "ethpm-config.yaml").read_text()
Expand Down Expand Up @@ -77,6 +85,7 @@ def patch_vault_version(version):
return Vault
else:
source = VAULT_SOURCE_CODE.replace(PACKAGE_VERSION, version)
print(source)
return compile_source(source).Vyper

return patch_vault_version
Expand Down Expand Up @@ -197,3 +206,26 @@ def sign_vault_permit(
@pytest.fixture(scope="function", autouse=True)
def shared_setup(fn_isolation):
pass


@pytest.fixture(scope="module", autouse=True)
def impersonate():
yield Impersonate


class Impersonate:
def __init__(self, account):
self.account = account

def __enter__(self):
if chain.id == 31337:
web3.provider.make_request(
"anvil_impersonateAccount", [self.account.address]
)
web3.provider.make_request(
"anvil_setBalance", [self.account.address, 10**19]
)

def __exit__(self, *args):
if chain.id == 31337:
web3.provider.make_request("anvil_stopImpersonatingAccount", [])
52 changes: 18 additions & 34 deletions tests/functional/registry/test_deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def test_deployment_management(
):
v1_token = create_token()
# No deployments yet for token
with brownie.reverts():
registry.latestVault(v1_token)
# with brownie.reverts(): # Issue with anvil.
# registry.latestVault(v1_token)

# Token tracking state variables should start off uninitialized
assert registry.tokens(0) == ZERO_ADDRESS
Expand Down Expand Up @@ -52,11 +52,8 @@ def test_deployment_management(

# You can deploy proxy Vaults, linked to the latest release
assert registry.numTokens() == 1
proxy_vault = Vault.at(
registry.newVault(
v1_token, guardian, rewards, "", "", {"from": gov}
).return_value
)
tx = registry.newVault(v1_token, guardian, rewards, "", "", {"from": gov})
proxy_vault = Vault.at(tx.events["NewVault"]["vault"])
assert proxy_vault.apiVersion() == v2_vault.apiVersion() == "2.0.0"
assert proxy_vault.rewards() == rewards
assert proxy_vault.guardian() == guardian
Expand All @@ -67,11 +64,9 @@ def test_deployment_management(

# You can deploy proxy Vaults, linked to a previous release
v2_token = create_token()
proxy_vault = Vault.at(
registry.newVault(
v2_token, guardian, rewards, "", "", 1, {"from": gov}
).return_value
)
tx = registry.newVault(v2_token, guardian, rewards, "", "", 1, {"from": gov})
proxy_vault = Vault.at(tx.events["NewVault"]["vault"])

assert proxy_vault.apiVersion() == v1_vault.apiVersion() == "1.0.0"
assert proxy_vault.rewards() == rewards
assert proxy_vault.guardian() == guardian
Expand Down Expand Up @@ -100,15 +95,14 @@ def test_experimental_deployments(
registry.newExperimentalVault(token, rando, rando, rando, "", "", {"from": rando})

# You can make as many experiments as you want with same api version
experimental_vault = Vault.at(
registry.newExperimentalVault(
token, rando, rando, rando, "", "", {"from": rando}
).return_value
tx = registry.newExperimentalVault(
token, rando, rando, rando, "", "", {"from": rando}
)
experimental_vault = Vault.at(tx.events["NewExperimentalVault"]["vault"])

# Experimental Vaults do not count towards deployments
with brownie.reverts():
registry.latestVault(token)
# with brownie.reverts(): # Issue with anvil.
# registry.latestVault(token)

# You can't endorse a vault if governance isn't set properly
with brownie.reverts():
Expand All @@ -132,33 +126,23 @@ def test_experimental_deployments(
assert registry.numTokens() == 1

# You can't endorse a vault if it would overwrite a current deployment
experimental_vault = Vault.at(
registry.newExperimentalVault(
token, gov, gov, gov, "", "", {"from": rando}
).return_value
)
tx = registry.newExperimentalVault(token, gov, gov, gov, "", "", {"from": rando})
experimental_vault = Vault.at(tx.events["NewExperimentalVault"]["vault"])
with brownie.reverts():
registry.endorseVault(experimental_vault, {"from": gov})

# You can only endorse a vault if it creates a new deployment
v2_vault = create_vault(version="2.0.0")
registry.newRelease(v2_vault, {"from": gov})

experimental_vault = Vault.at(
registry.newExperimentalVault(
token, gov, gov, gov, "", "", {"from": rando}
).return_value
)
tx = registry.newExperimentalVault(token, gov, gov, gov, "", "", {"from": rando})
experimental_vault = Vault.at(tx.events["NewExperimentalVault"]["vault"])
registry.endorseVault(experimental_vault, {"from": gov})
assert registry.latestVault(token) == experimental_vault

# Can create an experiment and endorse it targeting a previous version
token = create_token()
experimental_vault = Vault.at(
registry.newExperimentalVault(
token, gov, gov, gov, "", "", 1, {"from": rando}
).return_value
)
tx = registry.newExperimentalVault(token, gov, gov, gov, "", "", 1, {"from": rando})
experimental_vault = Vault.at(tx.events["NewExperimentalVault"]["vault"])
registry.endorseVault(experimental_vault, 1, {"from": gov})
assert registry.latestVault(token) == experimental_vault

Expand Down
4 changes: 2 additions & 2 deletions tests/functional/registry/test_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

def test_release_management(gov, registry, create_vault, rando):
# No releases yet
with brownie.reverts():
registry.latestRelease()
# with brownie.reverts(): # Issue with anvil.
# registry.latestRelease()

# Not just anyone can create a new Release
vault = create_vault()
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/strategy/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_strategy_deployment(strategist, vault, TestStrategy):
def test_strategy_no_reinit(strategist, vault, TestStrategy):
strategy = strategist.deploy(TestStrategy, vault)

with brownie.reverts("Strategy already initialized"):
with brownie.reverts():
strategy.initialize(vault, strategist, strategist, strategist)


Expand Down
10 changes: 5 additions & 5 deletions tests/functional/strategy/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,15 @@ def test_sweep(gov, vault, strategy, rando, token, other_token):
token.transfer(strategy, token.balanceOf(gov), {"from": gov})
assert token.address == strategy.want()
assert token.balanceOf(strategy) > 0
with brownie.reverts("!want"):
with brownie.reverts():
strategy.sweep(token, {"from": gov})

# Vault share token doesn't work
with brownie.reverts("!shares"):
with brownie.reverts():
strategy.sweep(vault.address, {"from": gov})

# Protected token doesn't work
with brownie.reverts("!protected"):
with brownie.reverts():
strategy.sweep(strategy.protectedToken(), {"from": gov})

# But any other random token works
Expand Down Expand Up @@ -168,12 +168,12 @@ def test_reject_ether(gov, strategy):
("setEmergencyExit", []),
("sweep", ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"]),
]:
with brownie.reverts("Cannot send ether to nonpayable function"):
with brownie.reverts():
# NOTE: gov can do anything
getattr(strategy, func)(*args, {"from": gov, "value": 1})

# Fallback fails too
with brownie.reverts("Cannot send ether to nonpayable function"):
with brownie.reverts():
gov.transfer(strategy, 1)


Expand Down
5 changes: 3 additions & 2 deletions tests/functional/strategy/test_withdrawal.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import brownie


def test_withdraw(chain, gov, token, vault, strategy, rando):
def test_withdraw(impersonate, chain, gov, token, vault, strategy, rando):
token.approve(vault, token.balanceOf(gov), {"from": gov})
vault.deposit(token.balanceOf(gov) // 2, {"from": gov})
chain.sleep(8640)
strategy.harvest({"from": gov}) # Seed some debt in there
assert strategy.estimatedTotalAssets() > 0

balance = strategy.estimatedTotalAssets()
strategy.withdraw(balance // 2, {"from": vault.address})
with impersonate(vault):
strategy.withdraw(balance // 2, {"from": vault.address})
# NOTE: This may be +1 more than just dividing it
assert strategy.estimatedTotalAssets() == balance - balance // 2

Expand Down
12 changes: 8 additions & 4 deletions tests/functional/vault/test_losses.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,16 @@ def test_losses(chain, vault, strategy, gov, token):
assert params["totalDebt"] == 0


def test_total_loss(chain, vault, strategy, gov, token):
def test_total_loss(impersonate, chain, vault, strategy, gov, token):
vault.addStrategy(strategy, 10_000, 0, 2**256 - 1, 1_000, {"from": gov})
token.approve(vault, 2**256 - 1, {"from": gov})
vault.deposit(5000, {"from": gov})
strategy.harvest({"from": gov})
assert token.balanceOf(strategy) == 5000

# send all our tokens back to the token contract
token.transfer(token, token.balanceOf(strategy), {"from": strategy})
with impersonate(strategy):
token.transfer(token, token.balanceOf(strategy), {"from": strategy})

chain.sleep(1)
strategy.harvest({"from": gov})
Expand All @@ -81,7 +82,9 @@ def test_total_loss(chain, vault, strategy, gov, token):
assert params["debtRatio"] == 0


def test_loss_should_be_removed_from_locked_profit(chain, vault, strategy, gov, token):
def test_loss_should_be_removed_from_locked_profit(
impersonate, chain, vault, strategy, gov, token
):
vault.setLockedProfitDegradation(1e10, {"from": gov})

vault.addStrategy(strategy, 1000, 0, 1000, 0, {"from": gov})
Expand All @@ -96,7 +99,8 @@ def test_loss_should_be_removed_from_locked_profit(chain, vault, strategy, gov,

assert vault.lockedProfit() == 90 # 100 - performance fees

token.transfer(token, 40, {"from": strategy})
with impersonate(strategy):
token.transfer(token, 40, {"from": strategy})
chain.sleep(1)
strategy.harvest({"from": gov})
assert vault.lockedProfit() == 50
Loading

0 comments on commit 6903c37

Please sign in to comment.