Skip to content

Commit a2e1468

Browse files
feat: add update_entity to AsyncArkivModule
1 parent 6bd046b commit a2e1468

File tree

3 files changed

+131
-14
lines changed

3 files changed

+131
-14
lines changed

src/arkiv/module_async.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
Operations,
2323
TransactionReceipt,
2424
TxHash,
25+
UpdateOp,
2526
)
2627
from .utils import merge_annotations, to_tx_params
2728

@@ -101,6 +102,50 @@ async def create_entity(
101102
entity_key = create.entity_key
102103
return entity_key, receipt.tx_hash
103104

105+
async def update_entity(
106+
self,
107+
entity_key: EntityKey,
108+
payload: bytes | None = None,
109+
annotations: Annotations | None = None,
110+
btl: int | None = None,
111+
tx_params: TxParams | None = None,
112+
) -> TxHash:
113+
"""
114+
Update an existing entity on the Arkiv storage contract (async).
115+
116+
Args:
117+
entity_key: The entity key of the entity to update
118+
payload: Optional new data payload for the entity, existing payload will be replaced
119+
annotations: Optional new key-value annotations, existing annotations will be replaced
120+
btl: Blocks to live (default: self.btl_default, ~30 minutes with 2s blocks)
121+
tx_params: Optional additional transaction parameters
122+
123+
Returns:
124+
Transaction hash of the update operation
125+
"""
126+
# Create the update operation
127+
payload, annotations, btl = self._check_and_set_argument_defaults(
128+
payload, annotations, btl
129+
)
130+
update_op = UpdateOp(
131+
entity_key=entity_key,
132+
payload=payload,
133+
annotations=annotations,
134+
btl=btl,
135+
)
136+
137+
# Wrap in Operations container and execute
138+
operations = Operations(updates=[update_op])
139+
receipt = await self.execute(operations, tx_params)
140+
141+
# Verify the update succeeded
142+
if len(receipt.updates) != 1:
143+
raise RuntimeError(
144+
f"Expected 1 update in receipt, got {len(receipt.updates)}"
145+
)
146+
147+
return receipt.tx_hash
148+
104149
async def entity_exists(self, entity_key: EntityKey) -> bool:
105150
"""
106151
Check if an entity exists storage (async).

tests/test_async_entity_update.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""Tests for async entity update functionality in AsyncArkivModule."""
2+
3+
import logging
4+
5+
import pytest
6+
7+
from arkiv import AsyncArkiv
8+
from arkiv.types import Annotations
9+
from arkiv.utils import check_entity_key
10+
11+
from .utils import check_tx_hash
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class TestAsyncEntityUpdate:
17+
"""Test cases for async update_entity function."""
18+
19+
@pytest.mark.asyncio
20+
async def test_async_update_entity_basic(
21+
self, async_arkiv_client_http: AsyncArkiv
22+
) -> None:
23+
"""Test updating an entity with async client."""
24+
# Create entity
25+
original_payload = b"Original payload"
26+
original_annotations = Annotations({"status": "initial", "version": 1})
27+
entity_key, _tx_hash = await async_arkiv_client_http.arkiv.create_entity(
28+
payload=original_payload, annotations=original_annotations, btl=100
29+
)
30+
31+
# Update entity
32+
new_payload = b"Updated payload"
33+
new_annotations = Annotations({"status": "updated", "version": 2})
34+
update_tx_hash = await async_arkiv_client_http.arkiv.update_entity(
35+
entity_key=entity_key,
36+
payload=new_payload,
37+
annotations=new_annotations,
38+
btl=150,
39+
)
40+
41+
# Verify update transaction hash
42+
check_tx_hash("test_async_update_entity_basic", update_tx_hash)
43+
44+
# Verify entity was updated
45+
entity = await async_arkiv_client_http.arkiv.get_entity(entity_key)
46+
assert entity.payload == new_payload, "Payload should be updated"
47+
assert entity.annotations == new_annotations, "Annotations should be updated"
48+
49+
logger.info(f"Updated async entity: {entity_key} (tx: {update_tx_hash})")
50+
51+
@pytest.mark.asyncio
52+
async def test_async_update_entities_sequentially(
53+
self, async_arkiv_client_http: AsyncArkiv
54+
) -> None:
55+
"""Test updating multiple entities sequentially."""
56+
# Create multiple entities
57+
entity_keys = []
58+
for i in range(3):
59+
entity_key, _tx_hash = await async_arkiv_client_http.arkiv.create_entity(
60+
payload=f"Entity {i}".encode(),
61+
annotations=Annotations({"index": i, "version": 1}),
62+
)
63+
entity_keys.append(entity_key)
64+
65+
# Update all entities sequentially
66+
for i, entity_key in enumerate(entity_keys):
67+
update_tx_hash = await async_arkiv_client_http.arkiv.update_entity(
68+
entity_key=entity_key,
69+
payload=f"Updated entity {i}".encode(),
70+
annotations=Annotations({"index": i, "version": 2}),
71+
)
72+
# Verify individual entity_key and tx_hash formats
73+
check_entity_key(entity_key, f"test_async_update_entities_sequentially_{i}")
74+
check_tx_hash(f"test_async_update_entities_sequentially_{i}", update_tx_hash)
75+
logger.info(f"Updated entity {i + 1}/3: {entity_key}")
76+
77+
# Verify all updates
78+
for i, entity_key in enumerate(entity_keys):
79+
entity = await async_arkiv_client_http.arkiv.get_entity(entity_key)
80+
assert entity.payload == f"Updated entity {i}".encode()
81+
assert entity.annotations == Annotations({"index": i, "version": 2})
82+
83+
logger.info("Successfully updated 3 entities sequentially")

tests/test_entity_create.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,16 @@
77

88
from arkiv.client import Arkiv
99
from arkiv.contract import STORAGE_ADDRESS
10-
from arkiv.types import Annotations, CreateOp, Operations, TxHash
10+
from arkiv.types import Annotations, CreateOp, Operations
1111
from arkiv.utils import check_entity_key, to_receipt, to_tx_params
1212

13+
from .utils import check_tx_hash
14+
1315
logger = logging.getLogger(__name__)
1416

1517
TX_SUCCESS = 1
1618

1719

18-
def check_tx_hash(label: str, tx_hash: TxHash) -> None:
19-
"""Check transaction hash validity."""
20-
logger.info(f"{label}: Checking transaction hash {tx_hash}")
21-
assert tx_hash is not None, f"{label}: Transaction hash should not be None"
22-
assert isinstance(tx_hash, str), (
23-
f"{label}: Transaction hash should be a string (TxHash)"
24-
)
25-
assert len(tx_hash) == 66, (
26-
f"{label}: Transaction hash should be 66 characters long (0x + 64 hex)"
27-
)
28-
assert tx_hash.startswith("0x"), f"{label}: Transaction hash should start with 0x"
29-
30-
3120
class TestEntityCreate:
3221
"""Test cases for create_entity function."""
3322

0 commit comments

Comments
 (0)