Skip to content

Commit

Permalink
add tests for BoxRef implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
boblat committed Jul 15, 2024
1 parent c4c946a commit b5800d1
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 29 deletions.
51 changes: 31 additions & 20 deletions src/algopy_testing/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def length(self) -> algopy.UInt64:
raise ValueError("Box has not been created")
return algopy.UInt64(len(context.get_box(self._key)))

def _cast_to_type(self, value: algopy.Bytes) -> _TValue:
def _cast_to_type(self, value: bytes) -> _TValue:
"""
assuming _TValue to be one of the followings:
- algopy.UInt64
Expand All @@ -121,7 +121,7 @@ def _cast_to_type(self, value: algopy.Bytes) -> _TValue:
return self._type.from_bytes(value) # type: ignore[attr-defined, no-any-return]
elif self._type is algopy.UInt64:
return algopy.op.btoi(value) # type: ignore[return-value]
return value # type: ignore[return-value]
return algopy.Bytes(value) # type: ignore[return-value]

def _cast_to_bytes(self, value: _TValue) -> algopy.Bytes:
"""
Expand Down Expand Up @@ -184,10 +184,10 @@ def create(self, *, size: algopy.UInt64 | int) -> bool:
if size_int > MAX_BOX_SIZE:
raise ValueError(f"Box size cannot exceed {MAX_BOX_SIZE}")

box_content, box_exists = self.maybe()
box_content, box_exists = self._maybe()
if box_exists and len(box_content) != size_int:
raise ValueError("Box already exists with a different size")
if not box_exists:
if box_exists:
return False
context = get_test_context()
context.set_box(self._key, b"\x00" * size_int)
Expand All @@ -211,15 +211,17 @@ def extract(
:arg start_index: The offset to start extracting bytes from
:arg length: The number of bytes to extract
"""
import algopy

box_content, box_exists = self.maybe()
box_content, box_exists = self._maybe()
start_int = int(start_index)
length_int = int(length)
if not box_exists:
raise ValueError("Box does not exist")
if (start_int + length_int) > len(box_content):
raise ValueError("Index out of bounds")
return box_content[start_int : start_int + length_int]
result = box_content[start_int : start_int + length_int]
return algopy.Bytes(result)

def resize(self, new_size: algopy.UInt64 | int) -> None:
"""
Expand All @@ -232,10 +234,10 @@ def resize(self, new_size: algopy.UInt64 | int) -> None:
new_size_int = int(new_size)

if new_size_int > MAX_BOX_SIZE:
raise ValueError("Invalid box size")
box_content, box_exists = self.maybe()
raise ValueError(f"Box size cannot exceed {MAX_BOX_SIZE}")
box_content, box_exists = self._maybe()
if not box_exists:
raise ValueError("Box does not exist")
raise ValueError("Box has not been created")
if new_size_int > len(box_content):
updated_content = box_content + b"\x00" * (new_size_int - len(box_content))
else:
Expand All @@ -251,9 +253,9 @@ def replace(self, start_index: algopy.UInt64 | int, value: algopy.Bytes | bytes)
:arg value: The bytes to be written
"""
context = get_test_context()
box_content, box_exists = self.maybe()
box_content, box_exists = self._maybe()
if not box_exists:
raise ValueError("Box does not exist")
raise ValueError("Box has not been created")
start = int(start_index)
length = len(value)
if (start + length) > len(box_content):
Expand Down Expand Up @@ -282,7 +284,7 @@ def splice(
import algopy

context = get_test_context()
box_content, box_exists = self.maybe()
box_content, box_exists = self._maybe()

start = int(start_index)
delete_count = int(length)
Expand Down Expand Up @@ -320,9 +322,9 @@ def get(self, *, default: algopy.Bytes | bytes) -> algopy.Bytes:
"""
import algopy

box_content, box_exists = self.maybe()
box_content, box_exists = self._maybe()
default_bytes = default if isinstance(default, algopy.Bytes) else algopy.Bytes(default)
return default_bytes if not box_exists else box_content
return default_bytes if not box_exists else algopy.Bytes(box_content)

def put(self, value: algopy.Bytes | bytes) -> None:
"""
Expand All @@ -331,19 +333,27 @@ def put(self, value: algopy.Bytes | bytes) -> None:
:arg value: The value to write to the box
"""
import algopy

box_content, box_exists = self.maybe()
box_content, box_exists = self._maybe()
if box_exists and len(box_content) != len(value):
raise ValueError("Box already exists with a different size")

context = get_test_context()
context.set_box(self._key, value)
content = value if isinstance(value, algopy.Bytes) else algopy.Bytes(value)
context.set_box(self._key, content)

def maybe(self) -> tuple[algopy.Bytes, bool]:
"""
Retrieve the contents of the box if it exists, and return a boolean indicating if the box
exists.
"""
import algopy

box_content, box_exists = self._maybe()
return (algopy.Bytes(box_content), box_exists)

def _maybe(self) -> tuple[bytes, bool]:
context = get_test_context()
box_exists = context.does_box_exist(self._key)
box_content = context.get_box(self._key)
Expand All @@ -356,7 +366,7 @@ def length(self) -> algopy.UInt64:
"""
import algopy

box_content, box_exists = self.maybe()
box_content, box_exists = self._maybe()
if not box_exists:
raise ValueError("Box has not been created")
return algopy.UInt64(len(box_content))
Expand Down Expand Up @@ -463,19 +473,20 @@ def length(self, key: _TKey) -> algopy.UInt64:
:arg key: The key of the box to get
"""
import algopy

context = get_test_context()
key_bytes = self._full_key(key)
box_exists = context.does_box_exist(key_bytes)
if not box_exists:
raise ValueError("Box has not been created")
box_content_bytes = context.get_box(key_bytes)
return box_content_bytes.length
return algopy.UInt64(len(box_content_bytes))

def _full_key(self, key: _TKey) -> algopy.Bytes:
return self._key_prefix + self._cast_to_bytes(key)

def _cast_to_value_type(self, value: algopy.Bytes) -> _TValue:
def _cast_to_value_type(self, value: bytes) -> _TValue:
"""
assuming _TValue to be one of the followings:
- algopy.UInt64
Expand All @@ -490,7 +501,7 @@ def _cast_to_value_type(self, value: algopy.Bytes) -> _TValue:
return self._value_type.from_bytes(value) # type: ignore[attr-defined, no-any-return]
elif self._value_type is algopy.UInt64:
return algopy.op.btoi(value) # type: ignore[return-value]
return value # type: ignore[return-value]
return algopy.Bytes(value) # type: ignore[return-value]

def _cast_to_bytes(self, value: _TValue | _TKey) -> algopy.Bytes:
"""
Expand Down
10 changes: 4 additions & 6 deletions src/algopy_testing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def __init__(
self._scratch_spaces: dict[str, list[algopy.Bytes | algopy.UInt64 | bytes | int]] = {}
self._template_vars: dict[str, Any] = template_vars or {}
self._blocks: dict[int, dict[str, int]] = {}
self._boxes: dict[bytes, algopy.Bytes] = {}
self._boxes: dict[bytes, bytes] = {}
self._lsigs: dict[algopy.LogicSig, Callable[[], algopy.UInt64 | bool]] = {}
self._active_lsig_args: Sequence[algopy.Bytes] = []

Expand Down Expand Up @@ -1052,20 +1052,18 @@ def does_box_exist(self, name: algopy.Bytes | bytes) -> bool:
name_bytes = name if isinstance(name, bytes) else name.value
return name_bytes in self._boxes

def get_box(self, name: algopy.Bytes | bytes) -> algopy.Bytes:
def get_box(self, name: algopy.Bytes | bytes) -> bytes:
"""Get the content of a box."""
import algopy

name_bytes = name if isinstance(name, bytes) else name.value
return self._boxes.get(name_bytes, algopy.Bytes(b""))
return self._boxes.get(name_bytes, b"")

def set_box(self, name: algopy.Bytes | bytes, content: algopy.Bytes | bytes) -> None:
"""Set the content of a box."""
import algopy

name_bytes = name if isinstance(name, bytes) else name.value
content_bytes = content if isinstance(content, bytes) else content.value
self._boxes[name_bytes] = algopy.Bytes(content_bytes)
self._boxes[name_bytes] = content_bytes

def execute_logicsig(
self, lsig: algopy.LogicSig, lsig_args: Sequence[algopy.Bytes] | None = None
Expand Down
7 changes: 4 additions & 3 deletions src/algopy_testing/models/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,16 @@ def extract(
box_content = context.get_box(name_bytes)
if not box_content:
raise ValueError("Box does not exist")
return box_content[start : start + length]
result = box_content[start : start + length]
return algopy.Bytes(result)

@staticmethod
def get(a: algopy.Bytes | bytes, /) -> tuple[algopy.Bytes, bool]:
import algopy

context = get_test_context()
name_bytes = a.value if isinstance(a, algopy.Bytes) else a
box_content = context.get_box(name_bytes)
box_content = algopy.Bytes(context.get_box(name_bytes))
return box_content, bool(box_content)

@staticmethod
Expand All @@ -78,7 +79,7 @@ def put(a: algopy.Bytes | bytes, b: algopy.Bytes | bytes, /) -> None:
existing_content = context.get_box(name_bytes)
if existing_content and len(existing_content) != len(content):
raise ValueError("New content length does not match existing box length")
context.set_box(name_bytes, content)
context.set_box(name_bytes, algopy.Bytes(content))

@staticmethod
def replace(
Expand Down
Loading

0 comments on commit b5800d1

Please sign in to comment.