Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
fe44f8a
test: service functions
romanzac Nov 17, 2025
34ac24c
test: test_token_gated_community_membership
romanzac Nov 17, 2025
20af489
fix: chain id
romanzac Nov 18, 2025
99a17c4
test: test_token_gated_community_membership_with_valid_tokens
romanzac Nov 18, 2025
63526ce
fix: skip test because of issue 7114
romanzac Nov 18, 2025
44b1fa5
fix: refactor setup_backends
romanzac Nov 19, 2025
83e7615
fix: add member permission to admin test
romanzac Nov 20, 2025
6cca928
fix: shutdown backends in parallel
romanzac Nov 20, 2025
66df4f8
fix: comment
romanzac Nov 21, 2025
0fd1efc
fix: skip test for issue 7135
romanzac Nov 21, 2025
d45837c
test: for issue
romanzac Nov 24, 2025
53b73e0
fix: skip test for issue 7139
romanzac Nov 24, 2025
7e79e24
fix: reduce for PR size
romanzac Nov 25, 2025
94c7ac6
fix: undo parallel container shutdown
romanzac Nov 25, 2025
71a2741
fix: move enums to services wakuext
romanzac Nov 26, 2025
44920be
fix: stop_messenger removed
romanzac Nov 26, 2025
3c023fe
fix: use CommunityTokenPermissionType instead of int
romanzac Nov 26, 2025
f0b0f62
fix: add CommunityRoles enum
romanzac Nov 26, 2025
db6aef9
fix: reduce test names
romanzac Nov 26, 2025
d6f088d
fix: remove unnecessary get
romanzac Nov 26, 2025
5c2384b
fix: wait times
romanzac Nov 26, 2025
273413a
fix: enum values serialization
romanzac Nov 26, 2025
4a9b8a9
fix: wrap generate tokens
romanzac Nov 26, 2025
05c15ac
fix: log outside helper
romanzac Nov 26, 2025
8cdbbd1
fix: get_erc20_balance method to Foundry
romanzac Nov 26, 2025
5a21801
fix: add assertions to balance
romanzac Nov 26, 2025
0a27b7b
fix: refactor create_token_gated_community
romanzac Nov 26, 2025
5458525
fix: remove unnecessary reevaluation
romanzac Nov 26, 2025
bfa72dd
fix: make it prettier
romanzac Nov 26, 2025
ca91587
fix: remove unnecessary check
romanzac Nov 27, 2025
a38c354
test: refactor to use signals
romanzac Nov 27, 2025
50c9fde
fix: add signal
romanzac Nov 28, 2025
a284875
fix: skipp test because of issue 7161
romanzac Nov 28, 2025
7612706
fix: properly wait for signals
igor-sirotin Nov 28, 2025
0995b25
fix: remove unused self.non_member
romanzac Dec 4, 2025
099997d
fix: move fake_address
romanzac Dec 4, 2025
25e5eba
fix: remove token overrides
romanzac Dec 4, 2025
7ec6370
fix: refactor backend creation and token deployment
romanzac Dec 4, 2025
2579448
feat: python restartWalletReloadTimer
igor-sirotin Dec 4, 2025
fdc1226
feat: wakuext community methods
igor-sirotin Dec 4, 2025
6d37b08
feat: request_to_join_community takes list of addresses
igor-sirotin Dec 4, 2025
fc5226d
feat: request_to_join_with_signatures
igor-sirotin Dec 4, 2025
e5476aa
chore: remove test_membership_no_valid_tokens
igor-sirotin Dec 4, 2025
818113e
fix: working version of test_membership_with_valid_tokens
igor-sirotin Dec 4, 2025
bcd67e6
fix: linter
igor-sirotin Dec 5, 2025
eaa80a7
test: improve test_admin_token_permissions_with_valid_tokens
igor-sirotin Dec 5, 2025
f56605a
Merge branch 'develop' into test/token-gated-communities
romanzac Dec 5, 2025
8c229d4
fix: reintroduce negative test
romanzac Dec 5, 2025
04ee9d9
test: use signed request for admin perm test
romanzac Dec 5, 2025
7c4484c
fix: return admin perm test to be skipped
romanzac Dec 5, 2025
f38de11
feat: expose anvil port
igor-sirotin Dec 6, 2025
7c34ec3
fix: token_overrides, multicall_contract_address
igor-sirotin Dec 6, 2025
849dfac
fix: working test_admin_token_permissions_with_valid_tokens
igor-sirotin Dec 6, 2025
497c20a
fix: request_to_join_community default arguments
igor-sirotin Dec 6, 2025
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
20 changes: 20 additions & 0 deletions tests-functional/clients/foundry.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,23 @@ def get_archive(self, container_path):
return os.path.join(temp_dir, tar.getmembers()[0].name)

return temp_dir

def generate_tokens(self, controller_address, to_address, token_amount, private_key):
if not self.container:
raise Exception("Container not found")

generate_cmd = (
f"cast send {controller_address} 'generateTokens(address,uint256)' "
f"{to_address} {token_amount} --rpc-url http://anvil:8545 "
f"--private-key {private_key}"
)
result = self.container.exec_run(generate_cmd)
return result

def get_erc20_balance(self, token_address, owner_address):
if not self.container:
raise Exception("Container not found")

balance_cmd = f"cast call {token_address} 'balanceOf(address)' {owner_address} --rpc-url http://anvil:8545"
result = self.container.exec_run(balance_cmd)
return result
155 changes: 151 additions & 4 deletions tests-functional/clients/services/wakuext.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from enum import Enum
from typing import TypedDict, Union
from typing import TypedDict, Union, Optional

from clients.rpc import RpcClient
from clients.services.service import Service
Expand Down Expand Up @@ -88,6 +89,40 @@ class CommunityPermissionsAccess(Enum):
MANUAL_ACCEPT = 3


class CommunityTokenPermissionType(Enum):
BECOME_MEMBER = 1
BECOME_ADMIN = 2
BECOME_TOKEN_MASTER = 3
CAN_VIEW_CHANNEL = 4
CAN_VIEW_AND_POST_CHANNEL = 5


class CommunityTokenType(Enum):
ERC20 = 1
ERC721 = 2
ENS = 3


class CommunityTokenPrivilegesLevel(Enum):
OWNER_LEVEL = 1
MASTER_LEVEL = 2
COMMUNITY_LEVEL = 3


class CommunityRoles(Enum):
ROLE_NONE = 0
ROLE_OWNER = 1
ROLE_ADMIN = 4
ROLE_TOKEN_MASTER = 5


@dataclass
class SignParams:
data: bytes
account: str
password: str


class Error(Exception):
def __init__(self, message):
self.message = message
Expand Down Expand Up @@ -276,8 +311,15 @@ def fetch_community(self, community_key):
response = self.rpc_request("fetchCommunity", params)
return response

def request_to_join_community(self, community_id, address="fakeaddress"):
params = [{"communityId": community_id, "addressesToReveal": [address], "airdropAddress": address}]
def request_to_join_community(self, community_id: str, addresses_to_reveal: list[str] = [], signatures: list[str] = []):
params = [
{
"communityId": community_id,
"addressesToReveal": addresses_to_reveal,
"airdropAddress": addresses_to_reveal[0] if len(addresses_to_reveal) > 0 else "",
"signatures": signatures,
}
]
response = self.rpc_request("requestToJoinCommunity", params)
return response

Expand Down Expand Up @@ -341,7 +383,7 @@ def check_permissions_to_join_community(self, community_id: str):
response = self.rpc_request("checkPermissionsToJoinCommunity", params)
return response

def generate_joining_community_requests_for_signing(self, member_pub_key: str, community_id: str, addresses_to_reveal: list):
def generate_joining_community_requests_for_signing(self, member_pub_key: str, community_id: str, addresses_to_reveal: list[str]):
params = [member_pub_key, community_id, addresses_to_reveal]
response = self.rpc_request("generateJoiningCommunityRequestsForSigning", params)
return response
Expand Down Expand Up @@ -770,3 +812,108 @@ def get_verification_request_sent_to(self, contact_id: str):
params = [contact_id]
response = self.rpc_request("getVerificationRequestSentTo", params)
return response

def save_community_token(self, token_data: dict):
params = [token_data]
response = self.rpc_request("saveCommunityToken", params)
return response

def add_community_token(self, community_id: str, chain_id: int, contract_address: str):
params = [community_id, chain_id, contract_address]
response = self.rpc_request("addCommunityToken", params)
return response

def create_history_archive_torrent_from_db(self, community_id: str, topics: list, start_date, end_date, partition_duration, encrypted: bool):
params = [
community_id,
topics,
start_date.isoformat() if hasattr(start_date, "isoformat") else str(start_date),
end_date.isoformat() if hasattr(end_date, "isoformat") else str(end_date),
partition_duration,
encrypted,
]
response = self.rpc_request("createHistoryArchiveTorrentFromDB", params)
return response

def load_history_archive_index_from_file(self, community_id: str):
params = [community_id]
response = self.rpc_request("loadHistoryArchiveIndexFromFile", params)
return response

def save_message_archive_id(self, community_id: str, archive_hash: str):
params = [community_id, archive_hash]
response = self.rpc_request("saveMessageArchiveID", params)
return response

def import_history_archives(self, community_id: str):
params = [community_id]
response = self.rpc_request("importHistoryArchives", params)
return response

def backup_community(self, community_id: str, clock: int):
params = [community_id, clock]
response = self.rpc_request("backupCommunity", params)
return response

def handle_local_backup_communities(self, communities_data: dict):
params = [communities_data]
response = self.rpc_request("handleLocalBackupCommunities", params)
return response

def generate_hash_ratchet_key(self, group_id: str):
params = [group_id]
response = self.rpc_request("generateHashRatchetKey", params)
return response

def create_community_token_permission(
self, community_id: str, permission_type: CommunityTokenPermissionType, token_criteria: list, chat_ids: Optional[list] = None
):
params = [
{
"communityId": community_id,
"type": permission_type.value,
"tokenCriteria": token_criteria,
}
]
if chat_ids:
params[0]["chatIds"] = chat_ids
response = self.rpc_request("createCommunityTokenPermission", params)
return response

def edit_community_token_permission(
self,
permission_id: str,
community_id: str,
permission_type: CommunityTokenPermissionType,
token_criteria: list,
chat_ids: Optional[list] = None,
):
params = [
{
"permissionId": permission_id,
"createCommunityTokenPermission": {
"communityId": community_id,
"type": permission_type.value,
"tokenCriteria": token_criteria,
},
}
]
if chat_ids:
params[0]["createCommunityTokenPermission"]["chatIds"] = chat_ids
response = self.rpc_request("editCommunityTokenPermission", params)
return response

def delete_community_token_permission(self, community_id: str, permission_id: str):
params = [
{
"communityId": community_id,
"permissionId": permission_id,
}
]
response = self.rpc_request("deleteCommunityTokenPermission", params)
return response

def sign_data(self, sign_params: list[dict]):
params = [sign_params]
response = self.rpc_request("signData", params)
return response
3 changes: 3 additions & 0 deletions tests-functional/clients/services/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,6 @@ def stop_suggested_routes_async_calculation(self):
def fetch_or_get_cached_wallet_balances(self, addresses: list, force_refresh: bool = False):
params = [addresses, force_refresh]
return self.rpc_request("fetchOrGetCachedWalletBalances", params)

def restart_wallet_reload_timer(self):
return self.rpc_request("restartWalletReloadTimer")
1 change: 1 addition & 0 deletions tests-functional/clients/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class SignalType(Enum):
CONNECTOR_DAPP_PERMISSION_GRANTED = "connector.dAppPermissionGranted"
CONNECTOR_DAPP_PERMISSION_REVOKED = "connector.dAppPermissionRevoked"
CONNECTOR_DAPP_CHAIN_ID_SWITCHED = "connector.dAppChainIdSwitched"
COMMUNITY_MEMBER_REEVALUATION_STATUS = "community.memberReevaluationStatus"


class WalletEventType(Enum):
Expand Down
2 changes: 1 addition & 1 deletion tests-functional/docker-compose.anvil.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
command:
- anvil --host 0.0.0.0 --block-time 2
ports:
- 8545
- "8545:8545"

foundry:
platform: linux/amd64
Expand Down
34 changes: 34 additions & 0 deletions tests-functional/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from clients.anvil import Anvil
from clients.contract_deployers.multicall3 import Multicall3Deployer
from clients.contract_deployers.snt import SNTDeployer
from clients.foundry import Foundry
from clients.status_backend import StatusBackend
from resources.constants import USE_IPV6
Expand Down Expand Up @@ -145,3 +146,36 @@ def foundry_client():
@pytest.fixture(scope="session")
def multicall3_deployer(foundry_client):
return Multicall3Deployer(foundry_client)


@pytest.fixture(scope="function")
def snt_deployment(foundry_client, request):
deployer = SNTDeployer(foundry_client)
request.cls.snt_deployer = deployer
request.cls.snt_address = deployer.snt_contract_address
request.cls.snt_controller_address = deployer.snt_token_controller_address
yield deployer


@pytest.fixture(scope="function")
def snt_token_overrides(snt_deployment):
return [{"symbol": "SNT", "address": snt_deployment.snt_contract_address}]


@pytest.fixture(scope="function", autouse=False)
def owner_backend(backend_new_profile, snt_token_overrides, multicall3_deployer):
return backend_new_profile(name="owner", token_overrides=snt_token_overrides, multicall_contract_address=multicall3_deployer.contract_address)


@pytest.fixture(scope="function", autouse=False)
def member_backend(backend_new_profile, snt_token_overrides, multicall3_deployer):
return backend_new_profile(name="member", token_overrides=snt_token_overrides, multicall_contract_address=multicall3_deployer.contract_address)


@pytest.fixture(scope="function", autouse=False)
def member_with_snt_backend(backend_new_profile, snt_token_overrides, multicall3_deployer):
return backend_new_profile(
name="member_with_snt",
token_overrides=snt_token_overrides,
multicall_contract_address=multicall3_deployer.contract_address,
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from uuid import uuid4

import pytest

from clients.signals import SignalType
from steps.messenger import MessengerSteps
from resources.enums import RequestToJoinState
from steps.messenger import MessengerSteps
from utils.retry_utils import retry_call


Expand All @@ -18,7 +20,7 @@ def setup_backends(self, backend_new_profile):
self.fetch_community(self.requester)

def test_pending_request_to_join_community_and_cancel(self):
req_resp = self.requester.wakuext_service.request_to_join_community(self.community_id, self.fake_address)
req_resp = self.requester.wakuext_service.request_to_join_community(self.community_id, [self.fake_address])
assert len(req_resp.get("requestsToJoinCommunity")) == 1
assert req_resp.get("requestsToJoinCommunity")[0].get("state") == RequestToJoinState.RequestToJoinStatePending.value
req_id = req_resp.get("requestsToJoinCommunity")[0].get("id")
Expand Down Expand Up @@ -67,7 +69,7 @@ def test_pending_request_to_join_community_and_cancel(self):
assert pending is None

def test_pending_request_to_join_community_and_accept(self):
req_resp = self.requester.wakuext_service.request_to_join_community(self.community_id, self.fake_address)
req_resp = self.requester.wakuext_service.request_to_join_community(self.community_id, [self.fake_address])
assert len(req_resp.get("requestsToJoinCommunity")) == 1
assert req_resp.get("requestsToJoinCommunity")[0].get("state") == RequestToJoinState.RequestToJoinStatePending.value
req_id = req_resp.get("requestsToJoinCommunity")[0].get("id")
Expand Down Expand Up @@ -120,7 +122,7 @@ def test_pending_request_to_join_community_and_accept(self):
assert all_non_approved == []

def test_pending_request_to_join_community_and_decline(self):
req_resp = self.requester.wakuext_service.request_to_join_community(self.community_id, self.fake_address)
req_resp = self.requester.wakuext_service.request_to_join_community(self.community_id, [self.fake_address])
assert len(req_resp.get("requestsToJoinCommunity")) == 1
assert req_resp.get("requestsToJoinCommunity")[0].get("state") == RequestToJoinState.RequestToJoinStatePending.value
req_id = req_resp.get("requestsToJoinCommunity")[0].get("id")
Expand Down Expand Up @@ -163,7 +165,7 @@ def test_pending_request_to_join_community_and_decline(self):
assert pending is None

def test_check_and_delete_pending_request_to_join_community(self):
req_resp = self.requester.wakuext_service.request_to_join_community(self.community_id, self.fake_address)
req_resp = self.requester.wakuext_service.request_to_join_community(self.community_id, [self.fake_address])
assert len(req_resp.get("requestsToJoinCommunity")) == 1
req_id = req_resp.get("requestsToJoinCommunity")[0].get("id")

Expand Down
Loading
Loading