Skip to content

Commit

Permalink
formalizes python package; extracts tests from nucypher
Browse files Browse the repository at this point in the history
  • Loading branch information
KPrasch committed Feb 16, 2024
1 parent b93ae12 commit fe06651
Show file tree
Hide file tree
Showing 10 changed files with 454 additions and 0 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ include README.md
include requirements.txt
include dev-requirements.txt
recursive-include deployment/artifacts *.json
recursive-exclude nucypher_contracts/tests *
recursive-exclude tests *
recursive-exclude * __pycache__
global-exclude *.py[cod]
Expand Down
Empty file added tests/__init__.py
Empty file.
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pytest
from ape import convert, project

from nucypher_contracts.domains import TACoDomain, ChainInfo


@pytest.fixture(scope="session")
def oz_dependency():
Expand Down
Empty file added tests/package/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions tests/package/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import pytest

from tests.utils import mock_registry_sources


@pytest.fixture(scope="module", autouse=True)
def auto_mock_registry_sources(module_mocker):
with mock_registry_sources(mocker=module_mocker):
yield
17 changes: 17 additions & 0 deletions tests/package/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from nucypher_contracts.domains import ChainInfo, TACoDomain

TEMPORARY_DOMAIN_NAME = ":temporary-domain:"

TESTERCHAIN_CHAIN_ID = 131277322940537

TESTERCHAIN_CHAIN_INFO = ChainInfo(
TESTERCHAIN_CHAIN_ID,
"eth-tester"
)

TEMPORARY_DOMAIN = TACoDomain(
name=TEMPORARY_DOMAIN_NAME,
eth_chain=TESTERCHAIN_CHAIN_INFO,
polygon_chain=TESTERCHAIN_CHAIN_INFO,
condition_chains=(TESTERCHAIN_CHAIN_INFO,),
)
85 changes: 85 additions & 0 deletions tests/package/test_registry_basics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import pytest

from nucypher_contracts.registry import ContractRegistry
from tests.utils import MockRegistrySource
from tests.package.constants import TEMPORARY_DOMAIN, TESTERCHAIN_CHAIN_ID


@pytest.fixture(scope="function")
def name():
return "TestContract"


@pytest.fixture(scope="function")
def address():
return "0xdeadbeef"


@pytest.fixture(scope="function")
def abi():
return ["fake", "data"]


@pytest.fixture(scope="function")
def record(name, address, abi):
record_data = {name: {"address": address, "abi": abi}}
return record_data


@pytest.fixture(scope="function")
def data(record):
_data = {TESTERCHAIN_CHAIN_ID: record}
return _data


@pytest.fixture(scope="function")
def source(data):
source = MockRegistrySource(domain=TEMPORARY_DOMAIN)
source.data = data
return source


@pytest.fixture(scope="function")
def registry(record, source):
registry = ContractRegistry(source=source)
return registry


def test_registry_id_consistency(registry, source):
new_registry = ContractRegistry(source=source)
new_registry._data = registry._data
assert new_registry.id == registry.id


def test_registry_name_search(registry, name, address, abi):
record = registry.search(chain_id=TESTERCHAIN_CHAIN_ID, contract_name=name)
assert len(record) == 4, "Registry record is the wrong length"
assert record.chain_id == TESTERCHAIN_CHAIN_ID
assert record.name == name
assert record.address == address
assert record.abi == abi


def test_registry_address_search(registry, name, address, abi):
record = registry.search(chain_id=TESTERCHAIN_CHAIN_ID, contract_address=address)
assert len(record) == 4, "Registry record is the wrong length"
assert record.chain_id == TESTERCHAIN_CHAIN_ID
assert record.name == name
assert record.address == address
assert record.abi == abi


def test_local_registry_unknown_contract_name_search(registry):
with pytest.raises(ContractRegistry.UnknownContract):
registry.search(
chain_id=TESTERCHAIN_CHAIN_ID, contract_name="this does not exist"
)


def test_local_contract_registry_ambiguous_search_terms(data, name, record, address):
data[TESTERCHAIN_CHAIN_ID]["fakeContract"] = record[name]
source = MockRegistrySource(domain=TEMPORARY_DOMAIN)
source.data = data
registry = ContractRegistry(source=source)
with pytest.raises(ContractRegistry.AmbiguousSearchTerms):
registry.search(chain_id=TESTERCHAIN_CHAIN_ID, contract_address=address)
138 changes: 138 additions & 0 deletions tests/package/test_registry_soures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import json

import pytest
import requests
from requests import Response

from nucypher_contracts import domains
from nucypher_contracts.registry import (
EmbeddedRegistrySource,
GithubRegistrySource,
LocalRegistrySource,
RegistrySource,
RegistrySourceManager,
)
from tests.package.constants import (
TEMPORARY_DOMAIN,
TEMPORARY_DOMAIN_NAME
)


@pytest.fixture(scope="function")
def registry_data():
_registry_data = {
"2958363635247": {
"TestContract": {"address": "0xdeadbeef", "abi": ["fake", "data"]},
"AnotherTestContract": {"address": "0xdeadbeef", "abi": ["fake", "data"]},
},
"393742274944474": {
"YetAnotherContract": {"address": "0xdeadbeef", "abi": ["fake", "data"]}
},
}
return _registry_data


@pytest.fixture(scope="function")
def mock_200_response(mocker, registry_data):
mock_response = Response()
mock_response.status_code = 200
mock_response._content = json.dumps(registry_data).encode("utf-8")
mocker.patch.object(requests, "get", return_value=mock_response)


@pytest.fixture(scope="function")
def test_registry_filepath(tmpdir, registry_data):
filepath = tmpdir.join("registry.json")
with open(filepath, "w") as f:
json.dump(registry_data, f)
yield filepath
filepath.remove()


@pytest.mark.usefixtures("mock_200_response")
def test_github_registry_source(registry_data):
source = GithubRegistrySource(domain=TEMPORARY_DOMAIN)
assert source.domain.name == TEMPORARY_DOMAIN_NAME
assert str(source.domain) == TEMPORARY_DOMAIN_NAME
assert bytes(source.domain) == TEMPORARY_DOMAIN_NAME.encode("utf-8")
data = source.get()
assert data == registry_data
assert source.data == registry_data
assert data == source.data


@pytest.mark.parametrize("domain", list(domains.SUPPORTED_DOMAINS.values()))
def test_get_actual_github_registry_file(domain):
source = GithubRegistrySource(domain=domain)
assert str(domain.eth_chain.id) in source.data
assert str(domain.polygon_chain.id) in source.data


def test_local_registry_source(registry_data, test_registry_filepath):
source = LocalRegistrySource(
filepath=test_registry_filepath, domain=TEMPORARY_DOMAIN
)
assert source.domain.name == TEMPORARY_DOMAIN_NAME
assert str(source.domain) == TEMPORARY_DOMAIN_NAME
assert bytes(source.domain) == TEMPORARY_DOMAIN_NAME.encode("utf-8")
data = source.get()
assert data == registry_data
assert source.data == registry_data
assert data == source.data


def test_embedded_registry_source(registry_data, test_registry_filepath, mocker):
mocker.patch.object(
EmbeddedRegistrySource,
"get_publication_endpoint",
return_value=test_registry_filepath,
)
source = EmbeddedRegistrySource(domain=TEMPORARY_DOMAIN)
assert source.domain.name == TEMPORARY_DOMAIN_NAME
assert str(source.domain) == TEMPORARY_DOMAIN_NAME
assert bytes(source.domain) == TEMPORARY_DOMAIN_NAME.encode("utf-8")
data = source.get()
assert data == registry_data
assert source.data == registry_data
assert data == source.data


def test_registry_source_manager_fallback(
registry_data, test_registry_filepath, mocker
):
github_source_get = mocker.patch.object(
GithubRegistrySource, "get", side_effect=RegistrySource.Unavailable
)
mocker.patch.object(
EmbeddedRegistrySource,
"get_publication_endpoint",
return_value=test_registry_filepath,
)
embedded_source_get = mocker.spy(EmbeddedRegistrySource, "get")
RegistrySourceManager._FALLBACK_CHAIN = (
GithubRegistrySource,
EmbeddedRegistrySource,
)
source_manager = RegistrySourceManager(domain=TEMPORARY_DOMAIN)
assert source_manager.domain.name == TEMPORARY_DOMAIN_NAME
assert str(source_manager.domain) == TEMPORARY_DOMAIN_NAME
assert bytes(source_manager.domain) == TEMPORARY_DOMAIN_NAME.encode("utf-8")

primary_sources = source_manager.get_primary_sources()
assert len(primary_sources) == 1
assert primary_sources[0] == GithubRegistrySource

source = source_manager.fetch_latest_publication()
github_source_get.assert_called_once()
embedded_source_get.assert_called_once()
assert source.data == registry_data
assert isinstance(source, EmbeddedRegistrySource)

mocker.patch.object(
EmbeddedRegistrySource,
"get_publication_endpoint",
side_effect=RegistrySource.Unavailable,
)

with pytest.raises(RegistrySourceManager.NoSourcesAvailable):
source_manager.fetch_latest_publication()
107 changes: 107 additions & 0 deletions tests/package/test_taco_domains.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import pytest

from nucypher_contracts import domains
from nucypher_contracts.domains import EthChain, PolygonChain


@pytest.fixture(scope="module")
def test_registry(module_mocker):
# override fixture which mocks domains.SUPPORTED_DOMAINS
yield


@pytest.fixture(scope="module", autouse=True)
def mock_condition_blockchains(module_mocker):
# override fixture which mocks domains.get_domain
yield


@pytest.mark.parametrize(
"eth_chain_test",
(
(EthChain.MAINNET, "mainnet", 1),
(EthChain.SEPOLIA, "sepolia", 11155111),
),
)
def test_eth_chains(eth_chain_test):
eth_chain, expected_name, expected_id = eth_chain_test
assert eth_chain.name == expected_name
assert eth_chain.id == expected_id


@pytest.mark.parametrize(
"poly_chain_test",
(
(PolygonChain.MAINNET, "polygon", 137),
(PolygonChain.MUMBAI, "mumbai", 80001),
),
)
def test_polygon_chains(poly_chain_test):
eth_chain, expected_name, expected_id = poly_chain_test
assert eth_chain.name == expected_name
assert eth_chain.id == expected_id


@pytest.mark.parametrize(
"taco_domain_test",
(
(
domains.MAINNET,
"mainnet",
EthChain.MAINNET,
PolygonChain.MAINNET,
(EthChain.MAINNET, PolygonChain.MAINNET),
),
(
domains.LYNX,
"lynx",
EthChain.SEPOLIA,
PolygonChain.MUMBAI,
(
EthChain.MAINNET,
EthChain.SEPOLIA,
PolygonChain.MUMBAI,
PolygonChain.MAINNET,
),
),
(
domains.TAPIR,
"tapir",
EthChain.SEPOLIA,
PolygonChain.MUMBAI,
(EthChain.SEPOLIA, PolygonChain.MUMBAI),
),
),
)
def test_taco_domain_info(taco_domain_test):
(
domain_info,
expected_name,
expected_eth_chain,
expected_polygon_chain,
expected_condition_chains,
) = taco_domain_test
assert domain_info.name == expected_name
assert domain_info.eth_chain == expected_eth_chain
assert domain_info.polygon_chain == expected_polygon_chain
assert domain_info.condition_chains == expected_condition_chains

assert domain_info.is_testnet == (expected_name != "mainnet")


@pytest.mark.parametrize(
"domain_name_test",
(
("mainnet", domains.MAINNET),
("lynx", domains.LYNX),
("tapir", domains.TAPIR),
),
)
def test_get_domain(domain_name_test):
domain_name, expected_domain_info = domain_name_test
assert domains.get_domain(domain_name) == expected_domain_info


def test_get_domain_unrecognized_domain_name():
with pytest.raises(domains.UnrecognizedTacoDomain):
domains.get_domain("5am_In_Toronto")
Loading

0 comments on commit fe06651

Please sign in to comment.