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
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file makes the tests/unit directory a Python package
1 change: 1 addition & 0 deletions tests/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file makes the tests/unit directory a Python package
Empty file.
5 changes: 5 additions & 0 deletions tests/unit/fixtures/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CHAIN_ID = 1315
ADDRESS = "0x1234567890123456789012345678901234567890"
TX_HASH = "0x0c0cce07beb64ccfbdd59da111f23084ab7c9e96a951f7381af49e792d014c04"
# STATE as bytes32 (32 bytes = 64 hex characters)
STATE = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
25 changes: 25 additions & 0 deletions tests/unit/fixtures/web3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from web3 import Web3
from unittest.mock import MagicMock, Mock

from tests.unit.fixtures.data import ADDRESS


mock_web3 = Mock(spec=Web3)
mock_web3.to_checksum_address = MagicMock(return_value=ADDRESS)

# Add eth attribute with contract method
mock_eth = Mock()


# Create a function that returns a new mock contract each time
def create_mock_contract(*args, **kwargs):
"""Create a new mock contract instance with address"""
mock_contract = Mock()
mock_contract.address = ADDRESS
mock_contract.encode_abi = MagicMock(return_value="0x00")
return mock_contract


# Set up the contract method to return new mock contracts
mock_eth.contract = create_mock_contract
mock_web3.eth = mock_eth
1 change: 1 addition & 0 deletions tests/unit/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file makes the tests/unit/resources directory a Python package
167 changes: 100 additions & 67 deletions tests/unit/resources/test_permission.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,108 @@
import os
import sys
import pytest
from unittest.mock import patch, MagicMock
from web3 import Web3
from dotenv import load_dotenv
from unittest.mock import Mock, patch

# Ensure the src directory is in the Python path
current_dir = os.path.dirname(__file__)
src_path = os.path.abspath(os.path.join(current_dir, '..', '..', '..'))
if src_path not in sys.path:
sys.path.append(src_path)
from story_protocol_python_sdk.resources.Permission import Permission
from tests.unit.fixtures.data import CHAIN_ID, ADDRESS, CHAIN_ID, STATE, TX_HASH
from tests.unit.fixtures.web3 import mock_web3

from src.story_protocol_python_sdk.resources.Permission import Permission

# Load environment variables from .env file
load_dotenv()
private_key = os.getenv('WALLET_PRIVATE_KEY')
rpc_url = os.getenv('RPC_PROVIDER_URL')
@pytest.fixture
def permission():
return Permission(mock_web3, ADDRESS, CHAIN_ID)

# Initialize Web3
web3 = Web3(Web3.HTTPProvider(rpc_url))

# Check if connected
if not web3.is_connected():
raise Exception("Failed to connect to Web3 provider")
class TestSetPermission:
def test_unregistered_ip_account(self, permission: Permission):
with patch.object(
permission.ip_asset_registry_client, "isRegistered", return_value=False
):
with pytest.raises(
Exception,
match="IP id with 0x1234567890123456789012345678901234567890 is not registered.",
):
permission.set_permission(ADDRESS, ADDRESS, ADDRESS, 1)

# Set up the account with the private key
account = web3.eth.account.from_key(private_key)
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
def test_invalid_signer_address(self, permission: Permission):
with patch.object(
permission.ip_asset_registry_client, "isRegistered", return_value=True
):
with pytest.raises(Exception, match="Invalid address: 0xInvalidAddress."):
permission.set_permission(ADDRESS, "0xInvalidAddress", ADDRESS, 1)

@pytest.fixture
def permission():
chain_id = 11155111 # Sepolia chain ID
return Permission(web3, account, chain_id)


def test_unregistered_ip_account(permission):
with patch.object(permission, '_is_registered', return_value=False):
with pytest.raises(ValueError, match="The IP account with id 0x0000000000000000000000000000000000000000 is not registered."):
permission.setPermission(ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, 1)

def test_invalid_signer_address(permission):
with pytest.raises(ValueError, match="The address 0xInvalidAddress that can call 'to' on behalf of the 'ip_asset' is not a valid address."):
permission.setPermission(ZERO_ADDRESS, "0xInvalidAddress", ZERO_ADDRESS, "0x11111111111111111111111111111")

def test_invalid_to_address(permission):
with pytest.raises(ValueError, match="The recipient of the transaction 0xInvalidAddress is not a valid address."):
permission.setPermission(ZERO_ADDRESS, ZERO_ADDRESS, "0xInvalidAddress", "0x11111111111111111111111111111")

def test_successful_transaction(permission):
ip_asset = "0x587AE719cACC8cC34188D9648d67CF885bE10558"
signer = "0x8059F63663576bE3605B3CcD30aaEb858C345640"
to = "0x2ac240293f12032E103458451dE8A8096c5A72E8"
func = "0x00000000"
permission_level = 1
tx_hash = "0x0c0cce07beb64ccfbdd59da111f23084ab7c9e96a951f7381af49e792d014c04"

with patch.object(permission, '_is_registered', return_value=True), \
patch('story_protocol_python_sdk.resources.IPAccount.IPAccount.execute', return_value={'txHash': tx_hash}):
response = permission.setPermission(ip_asset, signer, to, permission_level, func)
assert response['txHash'] == tx_hash

def test_transaction_request_fails(permission):
ip_asset = "0x587AE719cACC8cC34188D9648d67CF885bE10558"
signer = "0x8059F63663576bE3605B3CcD30aaEb858C345640"
to = "0x2ac240293f12032E103458451dE8A8096c5A72E8"
func = "0x00000000"
permission_level = 1

with patch.object(permission, '_is_registered', return_value=True), \
patch('story_protocol_python_sdk.resources.IPAccount.IPAccount.execute', side_effect=Exception("Transaction failed")):
with pytest.raises(Exception) as excinfo:
permission.setPermission(ip_asset, signer, to, permission_level, func)
assert str(excinfo.value) == "Transaction failed"
def test_invalid_to_address(self, permission: Permission):
with pytest.raises(Exception, match="Invalid address: 0xInvalidAddress."):
permission.set_permission(ADDRESS, ADDRESS, "0xInvalidAddress", 1)

def test_successful_transaction(self, permission: Permission):
with patch.object(
permission.ip_asset_registry_client, "isRegistered", return_value=True
), patch.object(
permission.ip_account, "execute", return_value={"tx_hash": TX_HASH}
):
response = permission.set_permission(ADDRESS, ADDRESS, ADDRESS, 1)
assert response["tx_hash"] == TX_HASH

def test_transaction_request_fails(self, permission: Permission):
with patch.object(
permission.ip_asset_registry_client, "isRegistered", return_value=True
), patch.object(
permission.ip_account,
"execute",
side_effect=Exception("Transaction failed"),
):
with pytest.raises(Exception, match="Transaction failed"):
permission.set_permission(ADDRESS, ADDRESS, ADDRESS, 1)


class TestSetAllPermissions:
def test_successful_transaction(self, permission: Permission):
with patch.object(
permission.ip_asset_registry_client, "isRegistered", return_value=True
), patch.object(
permission.ip_account, "execute", return_value={"tx_hash": TX_HASH}
):
response = permission.set_all_permissions(ADDRESS, ADDRESS, 1)
assert response["tx_hash"] == TX_HASH

def test_transaction_request_fails(self, permission: Permission):
with patch.object(
permission.ip_asset_registry_client, "isRegistered", return_value=True
), patch.object(
permission.ip_account,
"execute",
side_effect=Exception("Transaction failed"),
):
with pytest.raises(Exception, match="Transaction failed"):
permission.set_all_permissions(ADDRESS, ADDRESS, 1)


class TestCreateSetPermissionSignature:

def test_invalid_deadline(self, permission: Permission):
with pytest.raises(Exception, match="Invalid deadline value."):
permission.create_set_permission_signature(
ADDRESS, ADDRESS, ADDRESS, 1, deadline=-1
)

def test_successful_signature(self, permission: Permission):
mock_client = patch(
"story_protocol_python_sdk.resources.Permission.IPAccountImplClient"
).start()
mock_client.return_value.state.return_value = STATE
with patch.multiple(
permission,
ip_account=Mock(
execute_with_sig=Mock(return_value={"tx_hash": TX_HASH}),
),
sign_util=Mock(
get_permission_signature=Mock(
return_value={
"signature": "0x1234567890123456789012345678901234567890"
}
)
),
):
response = permission.create_set_permission_signature(
ADDRESS, ADDRESS, ADDRESS, 1
)
assert response["tx_hash"] == TX_HASH
34 changes: 20 additions & 14 deletions tests/unit/test_story_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,24 @@
from web3 import Web3
from dotenv import load_dotenv

# Ensure the src/story_protocol_python_sdk directory is in the Python path
current_dir = os.path.dirname(__file__)
src_path = os.path.abspath(os.path.join(current_dir, '..', '..', 'src'))
if src_path not in sys.path:
sys.path.append(src_path)

from story_protocol_python_sdk.story_client import StoryClient
from story_protocol_python_sdk.resources.IPAsset import IPAsset
from story_protocol_python_sdk.resources.License import License
from story_protocol_python_sdk.resources.Royalty import Royalty
from story_protocol_python_sdk.resources.IPAccount import IPAccount
from story_protocol_python_sdk.resources.Permission import Permission
from tests.unit.fixtures.data import CHAIN_ID

# Load environment variables from .env file
load_dotenv()
private_key = os.getenv('WALLET_PRIVATE_KEY')
rpc_url = os.getenv('RPC_PROVIDER_URL')
private_key = os.getenv("WALLET_PRIVATE_KEY")
rpc_url = os.getenv("RPC_PROVIDER_URL")

# Ensure the environment variables are set
if not private_key or not rpc_url:
raise ValueError("Please set WALLET_PRIVATE_KEY and RPC_PROVIDER_URL in the .env file")
raise ValueError(
"Please set WALLET_PRIVATE_KEY and RPC_PROVIDER_URL in the .env file"
)

# Initialize Web3
web3 = Web3(Web3.HTTPProvider(rpc_url))
Expand All @@ -36,48 +33,57 @@
# Set up the account with the private key
account = web3.eth.account.from_key(private_key)


@pytest.fixture
def story_client():
chain_id = 11155111 # Sepolia chain ID
return StoryClient(web3, account, chain_id)
return StoryClient(web3, account, CHAIN_ID)


def test_story_client_constructor(story_client):
assert story_client is not None
assert isinstance(story_client, StoryClient)


def test_story_client_transport_error():
with pytest.raises(ValueError):
StoryClient(None, account, chain_id=11155111)
StoryClient(None, account, chain_id=CHAIN_ID)


def test_story_client_account_missing():
with pytest.raises(ValueError):
StoryClient(web3, None, chain_id=11155111)
StoryClient(web3, None, chain_id=CHAIN_ID)


def test_story_client_wallet_initialization():
client = StoryClient(web3, account, chain_id=11155111)
client = StoryClient(web3, account, chain_id=CHAIN_ID)
assert client is not None
assert isinstance(client, StoryClient)


def test_ip_asset_client_getter(story_client):
ip_asset = story_client.IPAsset
assert ip_asset is not None
assert isinstance(ip_asset, IPAsset)


def test_license_client_getter(story_client):
license = story_client.License
assert license is not None
assert isinstance(license, License)


def test_royalty_client_getter(story_client):
royalty = story_client.Royalty
assert royalty is not None
assert isinstance(royalty, Royalty)


def test_ip_account_client_getter(story_client):
ip_account = story_client.IPAccount
assert ip_account is not None
assert isinstance(ip_account, IPAccount)


def test_permission_getter(story_client):
permission = story_client.Permission
assert permission is not None
Expand Down
Loading