Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 138 additions & 23 deletions tests/unit/resources/test_ip_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

# Constants
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000"
VALID_IP_ID = "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"
TX_HASH = "0xe87b172eee35872179ced53ea4f3f314b12cd0f5d0034e7f0ae3c4efce9ba6f1"

Expand All @@ -27,7 +28,7 @@ def __init__(self):
@staticmethod
def to_checksum_address(address):
if not is_address(address):
raise ValueError(f"The recipient of the transaction {address} is not a valid address")
raise ValueError(f"Invalid address: {address}")
return to_checksum_address(address)

@staticmethod
Expand Down Expand Up @@ -60,8 +61,6 @@ def ip_account(mock_web3, mock_account):
chain_id = 11155111 # Sepolia chain ID
return IPAccount(mock_web3, mock_account, chain_id)

@pytest.mark.unit
@pytest.mark.unit
@pytest.mark.unit
class TestExecute:
def test_invalid_recipient_address(self, ip_account):
Expand All @@ -84,10 +83,10 @@ def test_successful_transaction(self, ip_account):
mock_signed_txn = MagicMock()
mock_signed_txn.raw_transaction = b'raw_transaction_bytes'

# Mock transaction hash with hex method that returns hash WITHOUT '0x' prefix
# Mock transaction hash with hex method that returns hash
class MockTxHash:
def hex(self):
return TX_HASH[2:] # Remove '0x' prefix when returning the hash
return TX_HASH

mock_tx_hash = MockTxHash()

Expand All @@ -100,7 +99,7 @@ def hex(self):
patch.object(ip_account.web3.eth, 'wait_for_transaction_receipt', return_value={'status': 1}):

response = ip_account.execute(to_address, value, VALID_IP_ID, data)
assert response['txHash'] == TX_HASH[2:]
assert response['tx_hash'] == TX_HASH

def test_wait_for_transaction(self, ip_account):
to_address = "0xF9936a224b3Deb6f9A4645ccAfa66f7ECe83CF0A"
Expand All @@ -113,7 +112,7 @@ def test_wait_for_transaction(self, ip_account):

class MockTxHash:
def hex(self):
return TX_HASH[2:] # Remove '0x' prefix when returning the hash
return TX_HASH

mock_tx_hash = MockTxHash()

Expand All @@ -125,7 +124,7 @@ def hex(self):
patch.object(ip_account.web3.eth, 'wait_for_transaction_receipt', return_value={'status': 1}):

response = ip_account.execute(to_address, value, VALID_IP_ID, data, tx_options=tx_options)
assert response['txHash'] == TX_HASH[2:]
assert response['tx_hash'] == TX_HASH

def test_encoded_tx_data_only(self, ip_account):
to_address = "0xF9936a224b3Deb6f9A4645ccAfa66f7ECe83CF0A"
Expand Down Expand Up @@ -157,7 +156,7 @@ def test_encoded_tx_data_only(self, ip_account):
class TestExecuteWithSig:
def test_invalid_recipient_address(self, ip_account):
with pytest.raises(ValueError) as exc_info:
ip_account.executeWithSig(
ip_account.execute_with_sig(
VALID_IP_ID,
"0xInvalidAddress",
"0x11111111111111111111111111111",
Expand All @@ -180,7 +179,7 @@ def test_successful_transaction_with_sig(self, ip_account):

class MockTxHash:
def hex(self):
return TX_HASH[2:]
return TX_HASH

mock_tx_hash = MockTxHash()

Expand All @@ -191,8 +190,8 @@ def hex(self):
patch.object(ip_account.web3.eth, 'send_raw_transaction', return_value=mock_tx_hash), \
patch.object(ip_account.web3.eth, 'wait_for_transaction_receipt', return_value={'status': 1}):

response = ip_account.executeWithSig(VALID_IP_ID, to_address, data, signer, deadline, signature, value)
assert response['txHash'] == TX_HASH[2:]
response = ip_account.execute_with_sig(VALID_IP_ID, to_address, data, signer, deadline, signature, value)
assert response['tx_hash'] == TX_HASH

def test_wait_for_transaction_with_sig(self, ip_account):
to_address = "0xF9936a224b3Deb6f9A4645ccAfa66f7ECe83CF0A"
Expand All @@ -207,7 +206,7 @@ def test_wait_for_transaction_with_sig(self, ip_account):

class MockTxHash:
def hex(self):
return TX_HASH[2:] # Remove '0x' prefix when returning the hash
return TX_HASH

mock_tx_hash = MockTxHash()

Expand All @@ -218,16 +217,16 @@ def hex(self):
patch.object(ip_account.web3.eth, 'send_raw_transaction', return_value=mock_tx_hash), \
patch.object(ip_account.web3.eth, 'wait_for_transaction_receipt', return_value={'status': 1}):

response = ip_account.executeWithSig(
response = ip_account.execute_with_sig(
VALID_IP_ID, to_address, data, signer, deadline, signature,
tx_options=tx_options
)
assert response['txHash'] == TX_HASH[2:]
assert response['tx_hash'] == TX_HASH

class TestGetIpAccountNonce:
def test_invalid_ip_id(self, ip_account):
with pytest.raises(ValueError) as exc_info:
ip_account.getIpAccountNonce("0x123") # invalid address
ip_account.get_ip_account_nonce("0x123") # invalid address
assert "Invalid IP id address" in str(exc_info.value)

def test_successful_nonce_retrieval(self, ip_account):
Expand All @@ -237,25 +236,141 @@ def test_successful_nonce_retrieval(self, ip_account):
with patch('story_protocol_python_sdk.abi.IPAccountImpl.IPAccountImpl_client.IPAccountImplClient.state',
return_value=expected_nonce), \
patch('web3.eth.Eth.contract', return_value=MagicMock()):
nonce = ip_account.getIpAccountNonce(ip_id)
nonce = ip_account.get_ip_account_nonce(ip_id)
assert nonce == expected_nonce

class TestGetToken:
def test_invalid_ip_id(self, ip_account):
with pytest.raises(ValueError) as exc_info:
ip_account.getToken("0x123") # invalid address
ip_account.get_token("0x123") # invalid address
assert "Invalid IP id address" in str(exc_info.value)

def test_successful_token_retrieval(self, ip_account):
ip_id = Web3.to_checksum_address("0x73fcb515cee99e4991465ef586cfe2b072ebb512")
expected_token = {
'chainId': 1513,
'tokenContract': ZERO_ADDRESS,
'tokenId': 1
'chain_id': 1513,
'token_contract': ZERO_ADDRESS,
'token_id': 1
}

with patch('story_protocol_python_sdk.abi.IPAccountImpl.IPAccountImpl_client.IPAccountImplClient.token',
return_value=[1513, ZERO_ADDRESS, 1]), \
patch('web3.eth.Eth.contract', return_value=MagicMock()):
token = ip_account.getToken(ip_id)
assert token == expected_token
token = ip_account.get_token(ip_id)
assert token == expected_token

class TestTransferERC20:
def test_unregistered_ip_id(self, ip_account):
with patch.object(ip_account, '_is_registered', return_value=False):
with pytest.raises(ValueError) as exc_info:
ip_account.transfer_erc20(
ip_id=VALID_IP_ID,
tokens=[{
'address': ZERO_ADDRESS,
'target': ZERO_ADDRESS,
'amount': 1000
}]
)
assert f"IP id {VALID_IP_ID} is not registered" in str(exc_info.value)

def test_invalid_token_params(self, ip_account):
with patch.object(ip_account, '_is_registered', return_value=True):
with pytest.raises(ValueError) as exc_info:
ip_account.transfer_erc20(
ip_id=VALID_IP_ID,
tokens=[{
# Missing 'address'
'target': ZERO_ADDRESS,
'amount': 1000
}]
)
assert "Each token transfer must include 'address', 'target', and 'amount'" in str(exc_info.value)

def test_successful_transfer(self, ip_account):
tokens = [
{
'address': "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c",
'target': "0xF60cBF0Ea1A61567F1dDaf79A6219D20d189155c",
'amount': 1000000
},
{
'address': "0x2daAE3197Bc469Cb97B917aa460a12dD95c6627c",
'target': "0xF60cBF0Ea1A61567F1dDaf79A6219D20d189155c",
'amount': 2000000
}
]

mock_signed_txn = MagicMock()
mock_signed_txn.raw_transaction = b'raw_transaction_bytes'

class MockTxHash:
def hex(self):
return TX_HASH

mock_tx_hash = MockTxHash()

ip_account.account.sign_transaction = MagicMock(return_value=mock_signed_txn)

with patch.object(ip_account, '_is_registered', return_value=True), \
patch.object(ip_account.web3.eth, 'get_transaction_count', return_value=0), \
patch.object(ip_account.web3.eth, 'send_raw_transaction', return_value=mock_tx_hash), \
patch.object(ip_account.web3.eth, 'wait_for_transaction_receipt', return_value={'status': 1}):

response = ip_account.transfer_erc20(VALID_IP_ID, tokens)
assert response['tx_hash'] == TX_HASH

class TestSetIPMetadata:
def test_unregistered_ip_id(self, ip_account):
with patch.object(ip_account, '_is_registered', return_value=False):
with pytest.raises(ValueError) as exc_info:
ip_account.set_ip_metadata(
ip_id=VALID_IP_ID,
metadata_uri="ipfs://example",
metadata_hash=ZERO_HASH
)
assert f"IP id {VALID_IP_ID} is not registered" in str(exc_info.value)

def test_successful_metadata_update(self, ip_account):
metadata_uri = "ipfs://example"
metadata_hash = ZERO_HASH

mock_signed_txn = MagicMock()
mock_signed_txn.raw_transaction = b'raw_transaction_bytes'

class MockTxHash:
def hex(self):
return TX_HASH

mock_tx_hash = MockTxHash()

# Create a mock contract with a valid address
mock_contract = MagicMock()
mock_contract.address = "0xF9936a224b3Deb6f9A4645ccAfa66f7ECe83CF0A" # Use a valid address

ip_account.account.sign_transaction = MagicMock(return_value=mock_signed_txn)

with patch.object(ip_account, '_is_registered', return_value=True), \
patch.object(ip_account.web3.eth, 'get_transaction_count', return_value=0), \
patch.object(ip_account.web3.eth, 'send_raw_transaction', return_value=mock_tx_hash), \
patch.object(ip_account.web3.eth, 'wait_for_transaction_receipt', return_value={'status': 1}), \
patch.object(ip_account.web3.eth, 'contract', return_value=mock_contract), \
patch.object(ip_account.web3, 'is_address', return_value=True):

response = ip_account.set_ip_metadata(VALID_IP_ID, metadata_uri, metadata_hash)
assert response['tx_hash'] == TX_HASH

class TestOwner:
def test_invalid_ip_id(self, ip_account):
with pytest.raises(ValueError) as exc_info:
ip_account.owner("0x123") # invalid address
assert "Invalid IP id address" in str(exc_info.value)

def test_successful_owner_retrieval(self, ip_account):
ip_id = Web3.to_checksum_address("0x73fcb515cee99e4991465ef586cfe2b072ebb512")
expected_owner = "0xF60cBF0Ea1A61567F1dDaf79A6219D20d189155c"

with patch('story_protocol_python_sdk.abi.IPAccountImpl.IPAccountImpl_client.IPAccountImplClient.owner',
return_value=expected_owner), \
patch('web3.eth.Eth.contract', return_value=MagicMock()):
owner = ip_account.owner(ip_id)
assert owner == expected_owner
42 changes: 23 additions & 19 deletions tests/unit/resources/test_ip_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ def to_wei(number, unit):
@staticmethod
def is_address(address):
return is_address(address)

@staticmethod
def keccak(text=None):
return Web3.keccak(text=text)

def is_connected(self):
return True
Expand All @@ -59,14 +63,14 @@ class TestIPAssetRegister:
def test_register_invalid_deadline_type(self, ip_asset):
with patch.object(ip_asset, '_get_ip_id', return_value="0xd142822Dc1674154EaF4DDF38bbF7EF8f0D8ECe4"), \
patch.object(ip_asset, '_is_registered', return_value=False):
with pytest.raises(ValueError, match="Invalid deadline value."):
with pytest.raises(ValueError):
ip_asset.register(
nft_contract="0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c",
token_id=3,
deadline="error",
ip_metadata={
'ipMetadataURI': "1",
'ipMetadataHash': ZERO_HASH
'ip_metadata_uri': "1",
'ip_metadata_hash': ZERO_HASH
}
)

Expand All @@ -75,11 +79,11 @@ def test_register_already_registered(self, ip_asset):
token_id = 3
ip_id = "0xd142822Dc1674154EaF4DDF38bbF7EF8f0D8ECe4"

with patch.object(ip_asset.ip_asset_registry_client, 'ipId', return_value=ip_id), \
patch.object(ip_asset.ip_asset_registry_client, 'isRegistered', return_value=True):
with patch.object(ip_asset, '_get_ip_id', return_value=ip_id), \
patch.object(ip_asset, '_is_registered', return_value=True):
response = ip_asset.register(token_contract, token_id)
assert response['ipId'] == ip_id
assert response['txHash'] is None
assert response['ip_id'] == ip_id
assert response['tx_hash'] is None

def test_register_successful(self, ip_asset):
token_contract = "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"
Expand All @@ -90,7 +94,7 @@ def test_register_successful(self, ip_asset):

class MockTxHash:
def hex(self):
return tx_hash[2:]
return tx_hash

mock_tx_hash = MockTxHash()

Expand All @@ -105,11 +109,11 @@ def hex(self):
patch.object(ip_asset.web3.eth, 'get_transaction_count', return_value=0), \
patch.object(ip_asset.web3.eth, 'send_raw_transaction', return_value=mock_tx_hash), \
patch.object(ip_asset.web3.eth, 'wait_for_transaction_receipt', return_value={'status': 1, 'logs': []}), \
patch.object(ip_asset, '_parse_tx_ip_registered_event', return_value={'ipId': ip_id}):
patch.object(ip_asset, '_parse_tx_ip_registered_event', return_value={'ip_id': ip_id}):

result = ip_asset.register(token_contract, token_id)
assert result['txHash'] == tx_hash[2:]
assert result['ipId'] == ip_id
assert result['tx_hash'] == tx_hash
assert result['ip_id'] == ip_id

def test_register_with_metadata(self, ip_asset):
token_contract = "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"
Expand All @@ -118,17 +122,17 @@ def test_register_with_metadata(self, ip_asset):
tx_hash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"

metadata = {
'ipMetadataURI': "",
'ipMetadataHash': ZERO_HASH,
'nftMetadataURI': "",
'nftMetadataHash': ZERO_HASH,
'ip_metadata_uri': "",
'ip_metadata_hash': ZERO_HASH,
'nft_metadata_uri': "",
'nft_metadata_hash': ZERO_HASH,
}

calculated_deadline = 1000

class MockTxHash:
def hex(self):
return tx_hash[2:]
return tx_hash

mock_tx_hash = MockTxHash()

Expand All @@ -144,7 +148,7 @@ def hex(self):
patch.object(ip_asset.web3.eth, 'get_transaction_count', return_value=0), \
patch.object(ip_asset.web3.eth, 'send_raw_transaction', return_value=mock_tx_hash), \
patch.object(ip_asset.web3.eth, 'wait_for_transaction_receipt', return_value={'status': 1, 'logs': []}), \
patch.object(ip_asset, '_parse_tx_ip_registered_event', return_value={'ipId': ip_id}):
patch.object(ip_asset, '_parse_tx_ip_registered_event', return_value={'ip_id': ip_id, 'token_id': token_id}):

result = ip_asset.register(
nft_contract=token_contract,
Expand All @@ -153,5 +157,5 @@ def hex(self):
deadline=1000
)

assert result['txHash'] == tx_hash[2:]
assert result['ipId'] == ip_id
assert result['tx_hash'] == tx_hash
assert result['ip_id'] == ip_id
Loading