Skip to content

Commit 468fd84

Browse files
author
Matthias Zimmermann
committed
feat: add change_owner for AsyncArkiv
1 parent 49388ac commit 468fd84

File tree

2 files changed

+197
-1
lines changed

2 files changed

+197
-1
lines changed

src/arkiv/module_async.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66
from typing import TYPE_CHECKING
77

8-
from eth_typing import HexStr
8+
from eth_typing import ChecksumAddress, HexStr
99
from web3.types import TxParams, TxReceipt
1010

1111
from .events_async import AsyncEventFilter
@@ -19,6 +19,7 @@
1919
AsyncExtendCallback,
2020
AsyncUpdateCallback,
2121
Attributes,
22+
ChangeOwnerOp,
2223
DeleteOp,
2324
Entity,
2425
EntityKey,
@@ -130,6 +131,22 @@ async def extend_entity( # type: ignore[override]
130131
self._check_operations(receipt.extensions, "extend", 1)
131132
return receipt
132133

134+
async def change_owner( # type: ignore[override]
135+
self,
136+
entity_key: EntityKey,
137+
new_owner: ChecksumAddress,
138+
tx_params: TxParams | None = None,
139+
) -> TransactionReceipt:
140+
# Docstring inherited from ArkivModuleBase.change_owner
141+
# Create the change owner operation and execute TX
142+
change_owner_op = ChangeOwnerOp(entity_key=entity_key, new_owner=new_owner)
143+
operations = Operations(change_owners=[change_owner_op])
144+
receipt = await self.execute(operations, tx_params)
145+
146+
# Verify and return receipt
147+
self._check_operations(receipt.change_owners, "change_owner", 1)
148+
return receipt
149+
133150
async def delete_entity( # type: ignore[override]
134151
self,
135152
entity_key: EntityKey,
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
"""Tests for async entity ownership change functionality in AsyncArkivModule."""
2+
3+
import logging
4+
5+
import pytest
6+
7+
from arkiv.account import NamedAccount
8+
from arkiv.client import AsyncArkiv
9+
from arkiv.types import Attributes
10+
11+
from .utils import check_tx_hash
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class TestAsyncEntityChangeOwner:
17+
"""Test cases for async change_owner function."""
18+
19+
@pytest.mark.asyncio
20+
async def test_async_change_owner_simple(
21+
self, async_arkiv_client_http: AsyncArkiv, account_2: NamedAccount
22+
) -> None:
23+
"""Test changing ownership of a single entity asynchronously."""
24+
# Create an entity as the default account (alice)
25+
payload = b"Test entity for async ownership transfer"
26+
attributes: Attributes = Attributes(
27+
{"type": "test", "purpose": "async_ownership"}
28+
)
29+
btl = 100
30+
31+
entity_key, _ = await async_arkiv_client_http.arkiv.create_entity(
32+
payload=payload, attributes=attributes, btl=btl
33+
)
34+
35+
logger.info(f"Created entity {entity_key} for async ownership test")
36+
37+
# Verify initial owner
38+
entity = await async_arkiv_client_http.arkiv.get_entity(entity_key)
39+
initial_owner = entity.owner
40+
assert initial_owner == async_arkiv_client_http.eth.default_account, (
41+
"Initial owner should be the creator"
42+
)
43+
44+
# Change ownership to Bob
45+
new_owner = account_2.address
46+
change_receipt = await async_arkiv_client_http.arkiv.change_owner(
47+
entity_key, new_owner
48+
)
49+
50+
label = "async_change_owner"
51+
check_tx_hash(label, change_receipt)
52+
logger.info(
53+
f"{label}: Changed owner of entity {entity_key} to {new_owner}, "
54+
f"tx_hash: {change_receipt.tx_hash}"
55+
)
56+
57+
# Verify the new owner
58+
entity_after = await async_arkiv_client_http.arkiv.get_entity(entity_key)
59+
assert entity_after.owner == new_owner, (
60+
f"Owner should be {new_owner} after transfer"
61+
)
62+
assert entity_after.owner != initial_owner, (
63+
"Owner should have changed from initial owner"
64+
)
65+
66+
logger.info(f"{label}: Async ownership change successful")
67+
68+
@pytest.mark.asyncio
69+
async def test_async_change_owner_and_verify_entity_unchanged(
70+
self, async_arkiv_client_http: AsyncArkiv, account_2: NamedAccount
71+
) -> None:
72+
"""Test that changing ownership doesn't modify entity data asynchronously."""
73+
# Create an entity
74+
payload = b"Entity data should remain unchanged in async"
75+
attributes: Attributes = Attributes({"data": "important", "version": 1})
76+
btl = 150
77+
78+
entity_key, _ = await async_arkiv_client_http.arkiv.create_entity(
79+
payload=payload, attributes=attributes, btl=btl
80+
)
81+
82+
# Get entity before ownership change
83+
entity_before = await async_arkiv_client_http.arkiv.get_entity(entity_key)
84+
85+
# Change ownership
86+
new_owner = account_2.address
87+
change_receipt = await async_arkiv_client_http.arkiv.change_owner(
88+
entity_key, new_owner
89+
)
90+
check_tx_hash("async_change_owner_verify_data", change_receipt)
91+
92+
# Get entity after ownership change
93+
entity_after = await async_arkiv_client_http.arkiv.get_entity(entity_key)
94+
95+
# Verify only owner changed
96+
assert entity_after.owner == new_owner, "Owner should be updated"
97+
assert entity_after.payload == entity_before.payload, (
98+
"Payload should remain unchanged"
99+
)
100+
assert entity_after.attributes == entity_before.attributes, (
101+
"Attributes should remain unchanged"
102+
)
103+
assert entity_after.expires_at_block == entity_before.expires_at_block, (
104+
"Expiration should remain unchanged"
105+
)
106+
assert entity_after.entity_key == entity_before.entity_key, (
107+
"Entity key should remain unchanged"
108+
)
109+
110+
logger.info("Async ownership change preserved entity data")
111+
112+
@pytest.mark.asyncio
113+
async def test_async_change_owner_multiple_entities(
114+
self, async_arkiv_client_http: AsyncArkiv, account_2: NamedAccount
115+
) -> None:
116+
"""Test changing ownership of multiple entities asynchronously."""
117+
# Create multiple entities
118+
entity_keys = []
119+
for i in range(3):
120+
payload = f"Entity {i} for async ownership transfer".encode()
121+
attributes: Attributes = Attributes(
122+
{"index": i, "batch": "async_ownership"}
123+
)
124+
entity_key, _ = await async_arkiv_client_http.arkiv.create_entity(
125+
payload=payload, attributes=attributes, btl=100
126+
)
127+
entity_keys.append(entity_key)
128+
129+
logger.info(
130+
f"Created {len(entity_keys)} entities for async multiple ownership transfer"
131+
)
132+
133+
# Change ownership of all entities to Bob
134+
new_owner = account_2.address
135+
for i, entity_key in enumerate(entity_keys):
136+
receipt = await async_arkiv_client_http.arkiv.change_owner(
137+
entity_key, new_owner
138+
)
139+
check_tx_hash(f"async_change_owner_{i}", receipt)
140+
logger.info(
141+
f"Changed owner {i + 1}/{len(entity_keys)}: {entity_key} -> {new_owner}"
142+
)
143+
144+
# Verify all entities have new owner
145+
for entity_key in entity_keys:
146+
entity = await async_arkiv_client_http.arkiv.get_entity(entity_key)
147+
assert entity.owner == new_owner, (
148+
f"Entity {entity_key} should have new owner {new_owner}"
149+
)
150+
151+
logger.info("All entities successfully transferred to new owner asynchronously")
152+
153+
@pytest.mark.asyncio
154+
async def test_async_change_owner_to_same_owner(
155+
self,
156+
async_arkiv_client_http: AsyncArkiv,
157+
) -> None:
158+
"""Test changing ownership to the same owner asynchronously (should succeed as no-op)."""
159+
# Create an entity
160+
payload = b"Test entity for async same-owner transfer"
161+
attributes: Attributes = Attributes({"type": "test"})
162+
entity_key, _ = await async_arkiv_client_http.arkiv.create_entity(
163+
payload=payload, attributes=attributes, btl=100
164+
)
165+
166+
# Get current owner
167+
current_owner = async_arkiv_client_http.eth.default_account
168+
169+
# Change ownership to same owner
170+
receipt = await async_arkiv_client_http.arkiv.change_owner(
171+
entity_key, current_owner
172+
)
173+
check_tx_hash("async_change_owner_same", receipt)
174+
175+
# Verify owner is still the same
176+
entity = await async_arkiv_client_http.arkiv.get_entity(entity_key)
177+
assert entity.owner == current_owner, "Owner should remain unchanged"
178+
179+
logger.info("Async same-owner transfer completed successfully")

0 commit comments

Comments
 (0)