Skip to content

Commit

Permalink
test: adapt dictionaries before comparing in roundtrip unit-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
fsamier committed Oct 8, 2024
1 parent 488bc85 commit b47709b
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 3 deletions.
68 changes: 65 additions & 3 deletions tests/convert/test_convert_eip712_round_trip.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,81 @@
import json
from pathlib import Path
from typing import Any

import pytest
from eip712 import EIP712DAppDescriptor

from erc7730.common.pydantic import model_from_json_file_with_includes
from erc7730.common.pydantic import model_from_json_file_with_includes, model_to_json_str
from erc7730.convert.convert import convert_and_print_errors
from erc7730.convert.convert_eip712_to_erc7730 import EIP712toERC7730Converter
from erc7730.convert.convert_erc7730_input_to_resolved import ERC7730InputToResolved
from erc7730.convert.convert_erc7730_to_eip712 import ERC7730toEIP712Converter
from erc7730.model.display import FieldFormat
from erc7730.model.input.descriptor import InputERC7730Descriptor
from tests.assertions import assert_model_json_equals
from tests.assertions import assert_dict_equals, assert_model_json_equals
from tests.cases import path_id
from tests.dict_utils import del_by_path, is_in_path, map_by_path
from tests.files import ERC7730_EIP712_DESCRIPTORS, LEGACY_EIP712_DESCRIPTORS


def _adapt_and_comapre(input: InputERC7730Descriptor, output: InputERC7730Descriptor) -> None:
input_dict, output_dict = json.loads(model_to_json_str(input)), json.loads(model_to_json_str(output))

# $schema is not present in EIP-712
del_by_path(input_dict, "$schema")

# Addresses may be in EIP-55 format, convert to lower case in input
def _lowercase_addresses(deployments: list[dict[str, Any]]) -> Any:
for deployment in deployments:
if "address" in deployment:
deployment["address"] = deployment["address"].lower()
return deployments

map_by_path(
input_dict,
_lowercase_addresses,
"context",
"eip712",
"deployments",
)

# chainId and verifyingContract are always generated by the converter
# even when not explicitly present in the input
for key in "chainId", "verifyingContract":
path = "context", "eip712", "domain", key
if not is_in_path(input_dict, *path):
del_by_path(output_dict, *path)

# Some metadata cannot be stored in EIP712, and thus are lost with the roundtrip
del_by_path(input_dict, "metadata", "info")
del_by_path(input_dict, "metadata", "token")
del_by_path(input_dict, "metadata", "token")
del_by_path(input_dict, "context", "eip712", "domain", "version")

# Adapt formats objects to match EIP-712 conversion
def _cleanup_formats(formats: dict[str, Any]) -> Any:
for _, message in formats.items():
# Remove ERC-7730 specific fields
del_by_path(message, "$id")
del_by_path(message, "required")
del_by_path(message, "screens")
if "fields" in message:
for field in message["fields"]:
# Other formats are always converted to RAW
if "format" in field and field["format"] not in (
FieldFormat.AMOUNT,
FieldFormat.TOKEN_AMOUNT,
FieldFormat.DATE,
):
field["format"] = "raw"

return formats

map_by_path(input_dict, _cleanup_formats, "display", "formats")

assert_dict_equals(input_dict, output_dict)


@pytest.mark.parametrize("input_file", ERC7730_EIP712_DESCRIPTORS, ids=path_id)
def test_roundtrip_from_erc7730(input_file: Path) -> None:
input_erc7730_descriptor = InputERC7730Descriptor.load(input_file)
Expand All @@ -29,7 +91,7 @@ def test_roundtrip_from_erc7730(input_file: Path) -> None:
assert output_erc7730_descriptor is not None
if isinstance(output_erc7730_descriptor, dict):
pytest.skip("Multiple descriptors tests not supported")
assert_model_json_equals(input_erc7730_descriptor, output_erc7730_descriptor)
_adapt_and_comapre(input_erc7730_descriptor, output_erc7730_descriptor)


@pytest.mark.parametrize("input_file", LEGACY_EIP712_DESCRIPTORS, ids=path_id)
Expand Down
33 changes: 33 additions & 0 deletions tests/dict_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import contextlib
import operator
from collections.abc import Callable
from functools import reduce
from typing import Any


def get_by_path(root: dict[str, Any], *paths: str) -> Any:
"""Access a nested object in root by item sequence."""
return reduce(operator.getitem, list(paths), root)


def is_in_path(root: dict[str, Any], *paths: str) -> bool:
"""Check if a key-value in a nested object in root by item sequence exists."""
try:
get_by_path(root, *paths)
return True
except KeyError:
return False


def del_by_path(root: dict[str, Any], *paths: str) -> None:
"""Delete a key-value in a nested object in root by item sequence."""
path_list = list(paths)
with contextlib.suppress(KeyError):
del get_by_path(root, *path_list[:-1])[path_list[-1]]


def map_by_path(root: dict[str, Any], func: Callable[[Any], Any], *paths: str) -> None:
"""Map a function to a key-value in a nested object in root by item sequence."""
path_list = list(paths)
with contextlib.suppress(KeyError):
get_by_path(root, *path_list[:-1])[path_list[-1]] = func(get_by_path(root, *paths))

0 comments on commit b47709b

Please sign in to comment.