Skip to content

Commit

Permalink
test_: create private group tests (#6225)
Browse files Browse the repository at this point in the history
* test_: create private group tests

* test_: set privileged False for jenkins

* test_: run baseline tests in rpc suite

* test_: address review comments

* test_: address review comments
  • Loading branch information
fbarbu15 authored Dec 20, 2024
1 parent 0cf556b commit 810468a
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 157 deletions.
15 changes: 15 additions & 0 deletions tests-functional/clients/services/accounts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from clients.rpc import RpcClient
from clients.services.service import Service


class AccountService(Service):
def __init__(self, client: RpcClient):
super().__init__(client, "accounts")

def get_accounts(self):
response = self.rpc_request("getAccounts")
return response.json()

def get_account_keypairs(self):
response = self.rpc_request("getKeypairs")
return response.json()
2 changes: 1 addition & 1 deletion tests-functional/clients/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ def __init__(self, client: RpcClient, name: str):

def rpc_request(self, method: str, params=None):
full_method_name = f"{self.name}_{method}"
return self.rpc_client.rpc_request(full_method_name, params)
return self.rpc_client.rpc_valid_request(full_method_name, params)
11 changes: 11 additions & 0 deletions tests-functional/clients/services/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from clients.rpc import RpcClient
from clients.services.service import Service


class SettingsService(Service):
def __init__(self, client: RpcClient):
super().__init__(client, "settings")

def get_settings(self):
response = self.rpc_request("getSettings")
return response.json()
40 changes: 40 additions & 0 deletions tests-functional/clients/services/wakuext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from clients.rpc import RpcClient
from clients.services.service import Service


class WakuextService(Service):
def __init__(self, client: RpcClient):
super().__init__(client, "wakuext")

def send_contact_request(self, contact_id: str, message: str):
params = [{"id": contact_id, "message": message}]
response = self.rpc_request("sendContactRequest", params)
return response.json()

def accept_contact_request(self, request_id: str):
params = [{"id": request_id}]
response = self.rpc_request("acceptContactRequest", params)
return response.json()

def get_contacts(self):
response = self.rpc_request("contacts")
return response.json()

def send_message(self, contact_id: str, message: str):
params = [{"id": contact_id, "message": message}]
response = self.rpc_request("sendOneToOneMessage", params)
return response.json()

def start_messenger(self):
response = self.rpc_request("startMessenger")
json_response = response.json()

if "error" in json_response:
assert json_response["error"]["code"] == -32000
assert json_response["error"]["message"] == "messenger already started"
return

def create_group_chat_with_members(self, pubkey_list: list, group_chat_name: str):
params = [None, group_chat_name, pubkey_list]
response = self.rpc_request("createGroupChatWithMembers", params)
return response.json()
3 changes: 3 additions & 0 deletions tests-functional/clients/services/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ def __init__(self, client: RpcClient):
def get_balances_at_by_chain(self, chains: list, addresses: list, tokens: list):
params = [chains, addresses, tokens]
return self.rpc_request("getBalancesByChain", params)

def start_wallet(self):
return self.rpc_request("startWallet")
124 changes: 51 additions & 73 deletions tests-functional/clients/status_backend.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import io
import json
import logging
import string
import tarfile
import tempfile
import time
Expand All @@ -10,12 +11,15 @@
import docker
import docker.errors
import os

from tenacity import retry, stop_after_delay, wait_fixed
from clients.signals import SignalClient
from clients.services.wallet import WalletService
from clients.services.wakuext import WakuextService
from clients.services.accounts import AccountService
from clients.services.settings import SettingsService
from clients.signals import SignalClient, SignalType
from clients.rpc import RpcClient
from conftest import option
from resources.constants import user_1, DEFAULT_DISPLAY_NAME, USER_DIR
from docker.errors import APIError

NANOSECONDS_PER_SECOND = 1_000_000_000

Expand All @@ -24,22 +28,23 @@ class StatusBackend(RpcClient, SignalClient):

container = None

def __init__(self, await_signals=[]):
def __init__(self, await_signals=[], privileged=False):

if option.status_backend_url:
url = option.status_backend_url
else:
self.docker_client = docker.from_env()
host_port = random.choice(option.status_backend_port_range)

self.container = self._start_container(host_port)
self.container = self._start_container(host_port, privileged)
url = f"http://127.0.0.1:{host_port}"
option.status_backend_port_range.remove(host_port)

self.base_url = url
self.api_url = f"{url}/statusgo"
self.ws_url = f"{url}".replace("http", "ws")
self.rpc_url = f"{url}/statusgo/CallRPC"
self.public_key = ""

RpcClient.__init__(self, self.rpc_url)
SignalClient.__init__(self, self.ws_url, await_signals)
Expand All @@ -50,7 +55,12 @@ def __init__(self, await_signals=[]):
websocket_thread.daemon = True
websocket_thread.start()

def _start_container(self, host_port):
self.wallet_service = WalletService(self)
self.wakuext_service = WakuextService(self)
self.accounts_service = AccountService(self)
self.settings_service = SettingsService(self)

def _start_container(self, host_port, privileged):
docker_project_name = option.docker_project_name

timestamp = int(time.time() * 1000) # Keep in sync with run_functional_tests.sh
Expand All @@ -62,6 +72,7 @@ def _start_container(self, host_port):
container_args = {
"image": image_name,
"detach": True,
"privileged": privileged,
"name": container_name,
"labels": {"com.docker.compose.project": docker_project_name},
"entrypoint": [
Expand Down Expand Up @@ -182,21 +193,26 @@ def extract_data(self, path: str):
def create_account_and_login(
self,
data_dir=USER_DIR,
display_name=DEFAULT_DISPLAY_NAME,
display_name=None,
password=user_1.password,
):
self.display_name = (
display_name if display_name else f"DISP_NAME_{''.join(random.choices(string.ascii_letters + string.digits + '_-', k=10))}"
)
method = "CreateAccountAndLogin"
data = {
"rootDataDir": data_dir,
"kdfIterations": 256000,
"displayName": display_name,
"displayName": self.display_name,
"password": password,
"customizationColor": "primary",
"logEnabled": True,
"logLevel": "DEBUG",
}
data = self._set_proxy_credentials(data)
return self.api_valid_request(method, data)
resp = self.api_valid_request(method, data)
self.node_login_event = self.find_signal_containing_pattern(SignalType.NODE_LOGIN.value, event_pattern=self.display_name)
return resp

def restore_account_and_login(
self,
Expand Down Expand Up @@ -256,72 +272,34 @@ def restore_account_and_wait_for_rpc_client_to_start(self, timeout=60):
# ToDo: change this part for waiting for `node.login` signal when websockets are migrated to StatusBackend
while time.time() - start_time <= timeout:
try:
self.rpc_valid_request(method="accounts_getKeypairs")
self.accounts_service.get_account_keypairs()
return
except AssertionError:
time.sleep(3)
raise TimeoutError(f"RPC client was not started after {timeout} seconds")

@retry(stop=stop_after_delay(10), wait=wait_fixed(0.5), reraise=True)
def start_messenger(self, params=[]):
method = "wakuext_startMessenger"
response = self.rpc_request(method, params)
json_response = response.json()

if "error" in json_response:
assert json_response["error"]["code"] == -32000
assert json_response["error"]["message"] == "messenger already started"
return

self.verify_is_valid_json_rpc_response(response)

def start_wallet(self, params=[]):
method = "wallet_startWallet"
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

def get_settings(self, params=[]):
method = "settings_getSettings"
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

def get_accounts(self, params=[]):
method = "accounts_getAccounts"
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)
return response.json()

def get_pubkey(self, display_name):
response = self.get_accounts()
accounts = response.get("result", [])
for account in accounts:
if account.get("name") == display_name:
return account.get("public-key")
raise ValueError(f"Public key not found for display name: {display_name}")

def send_contact_request(self, contact_id: str, message: str):
method = "wakuext_sendContactRequest"
params = [{"id": contact_id, "message": message}]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)
return response.json()

def accept_contact_request(self, chat_id: str):
method = "wakuext_acceptContactRequest"
params = [{"id": chat_id}]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)
return response.json()

def get_contacts(self):
method = "wakuext_contacts"
response = self.rpc_request(method)
self.verify_is_valid_json_rpc_response(response)
return response.json()

def send_message(self, contact_id: str, message: str):
method = "wakuext_sendOneToOneMessage"
params = [{"id": contact_id, "message": message}]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)
return response.json()
def container_pause(self):
if not self.container:
raise RuntimeError("Container is not initialized.")
self.container.pause()
logging.info(f"Container {self.container.name} paused.")

def container_unpause(self):
if not self.container:
raise RuntimeError("Container is not initialized.")
self.container.unpause()
logging.info(f"Container {self.container.name} unpaused.")

def container_exec(self, command):
if not self.container:
raise RuntimeError("Container is not initialized.")
try:
exec_result = self.container.exec_run(cmd=["sh", "-c", command], stdout=True, stderr=True, tty=False)
if exec_result.exit_code != 0:
raise RuntimeError(f"Failed to execute command in container {self.container.id}:\n" f"OUTPUT: {exec_result.output.decode().strip()}")
return exec_result.output.decode().strip()
except APIError as e:
raise RuntimeError(f"API error during container execution: {str(e)}") from e

def find_public_key(self):
self.public_key = self.node_login_event.get("event", {}).get("settings", {}).get("public-key")
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
from time import sleep
from uuid import uuid4
import pytest
from test_cases import OneToOneMessageTestCase
from resources.constants import DEFAULT_DISPLAY_NAME
from test_cases import MessengerTestCase
from clients.signals import SignalType
from resources.enums import MessageContentType


@pytest.mark.rpc
class TestContactRequests(OneToOneMessageTestCase):
@pytest.mark.reliability
class TestContactRequests(MessengerTestCase):

@pytest.mark.rpc # until we have dedicated functional tests for this we can still run this test as part of the functional tests suite
@pytest.mark.dependency(name="test_contact_request_baseline")
def test_contact_request_baseline(self, execution_number=1):

await_signals = [
SignalType.MESSAGES_NEW.value,
SignalType.MESSAGE_DELIVERED.value,
]

message_text = f"test_contact_request_{execution_number}_{uuid4()}"
sender = self.initialize_backend(await_signals=self.await_signals)
receiver = self.initialize_backend(await_signals=self.await_signals)

sender = self.initialize_backend(await_signals=await_signals)
receiver = self.initialize_backend(await_signals=await_signals)

pk_sender = sender.get_pubkey(DEFAULT_DISPLAY_NAME)
pk_receiver = receiver.get_pubkey(DEFAULT_DISPLAY_NAME)

existing_contacts = receiver.get_contacts()
existing_contacts = receiver.wakuext_service.get_contacts()

if pk_sender in str(existing_contacts):
if sender.public_key in str(existing_contacts):
pytest.skip("Contact request was already sent for this sender<->receiver. Skipping test!!")

response = sender.send_contact_request(pk_receiver, message_text)
expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.CONTACT_REQUEST.value)
response = sender.wakuext_service.send_contact_request(receiver.public_key, message_text)
expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.CONTACT_REQUEST.value)[0]

messages_new_event = receiver.find_signal_containing_pattern(
SignalType.MESSAGES_NEW.value,
Expand All @@ -44,7 +34,9 @@ def test_contact_request_baseline(self, execution_number=1):
if "messages" in messages_new_event.get("event", {}):
signal_messages_texts.extend(message["text"] for message in messages_new_event["event"]["messages"] if "text" in message)

assert f"@{pk_sender} sent you a contact request" in signal_messages_texts, "Couldn't find the signal corresponding to the contact request"
assert (
f"@{sender.public_key} sent you a contact request" in signal_messages_texts
), "Couldn't find the signal corresponding to the contact request"

self.validate_signal_event_against_response(
signal_event=messages_new_event,
Expand All @@ -67,35 +59,40 @@ def test_multiple_contact_requests(self, execution_number):
@pytest.mark.dependency(depends=["test_contact_request_baseline"])
@pytest.mark.skip(reason="Skipping until add_latency is implemented")
def test_contact_request_with_latency(self):
with self.add_latency():
self.test_contact_request_baseline()
# with self.add_latency():
# self.test_contact_request_baseline()
# to be done in the next PR
pass

@pytest.mark.dependency(depends=["test_contact_request_baseline"])
@pytest.mark.skip(reason="Skipping until add_packet_loss is implemented")
def test_contact_request_with_packet_loss(self):
with self.add_packet_loss():
self.test_contact_request_baseline()
# with self.add_packet_loss():
# self.test_contact_request_baseline()
# to be done in the next PR
pass

@pytest.mark.dependency(depends=["test_contact_request_baseline"])
@pytest.mark.skip(reason="Skipping until add_low_bandwith is implemented")
def test_contact_request_with_low_bandwidth(self):
with self.add_low_bandwith():
self.test_contact_request_baseline()
# with self.add_low_bandwith():
# self.test_contact_request_baseline()
# to be done in the next PR
pass

@pytest.mark.dependency(depends=["test_contact_request_baseline"])
@pytest.mark.skip(reason="Skipping until node_pause is implemented")
def test_contact_request_with_node_pause_30_seconds(self):
await_signals = [
SignalType.MESSAGES_NEW.value,
SignalType.MESSAGE_DELIVERED.value,
]
sender = self.initialize_backend(await_signals=await_signals)
receiver = self.initialize_backend(await_signals=await_signals)
pk_receiver = receiver.get_pubkey(DEFAULT_DISPLAY_NAME)

with self.node_pause(receiver):
message_text = f"test_contact_request_{uuid4()}"
sender.send_contact_request(pk_receiver, message_text)
response = sender.wakuext_service.send_contact_request(receiver.public_key, message_text)
expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.CONTACT_REQUEST.value)[0]
sleep(30)
receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_text)
sender.wait_for_signal("messages.delivered")
receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=expected_message.get("id"))
sender.wait_for_signal(SignalType.MESSAGE_DELIVERED.value)
Loading

0 comments on commit 810468a

Please sign in to comment.