test_: create private group tests (#6225)

* 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
This commit is contained in:
fbarbu15 2024-12-20 13:08:11 +02:00 committed by GitHub
parent 0cf556bdb9
commit 810468a57f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 305 additions and 154 deletions

View File

@ -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()

View File

@ -9,4 +9,4 @@ class Service:
def rpc_request(self, method: str, params=None): def rpc_request(self, method: str, params=None):
full_method_name = f"{self.name}_{method}" 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)

View File

@ -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()

View File

@ -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()

View File

@ -9,3 +9,6 @@ class WalletService(Service):
def get_balances_at_by_chain(self, chains: list, addresses: list, tokens: list): def get_balances_at_by_chain(self, chains: list, addresses: list, tokens: list):
params = [chains, addresses, tokens] params = [chains, addresses, tokens]
return self.rpc_request("getBalancesByChain", params) return self.rpc_request("getBalancesByChain", params)
def start_wallet(self):
return self.rpc_request("startWallet")

View File

@ -1,6 +1,7 @@
import io import io
import json import json
import logging import logging
import string
import tarfile import tarfile
import tempfile import tempfile
import time import time
@ -10,12 +11,15 @@ import requests
import docker import docker
import docker.errors import docker.errors
import os import os
from clients.services.wallet import WalletService
from tenacity import retry, stop_after_delay, wait_fixed from clients.services.wakuext import WakuextService
from clients.signals import SignalClient from clients.services.accounts import AccountService
from clients.services.settings import SettingsService
from clients.signals import SignalClient, SignalType
from clients.rpc import RpcClient from clients.rpc import RpcClient
from conftest import option from conftest import option
from resources.constants import user_1, DEFAULT_DISPLAY_NAME, USER_DIR from resources.constants import user_1, DEFAULT_DISPLAY_NAME, USER_DIR
from docker.errors import APIError
NANOSECONDS_PER_SECOND = 1_000_000_000 NANOSECONDS_PER_SECOND = 1_000_000_000
@ -24,7 +28,7 @@ class StatusBackend(RpcClient, SignalClient):
container = None container = None
def __init__(self, await_signals=[]): def __init__(self, await_signals=[], privileged=False):
if option.status_backend_url: if option.status_backend_url:
url = option.status_backend_url url = option.status_backend_url
@ -32,7 +36,7 @@ class StatusBackend(RpcClient, SignalClient):
self.docker_client = docker.from_env() self.docker_client = docker.from_env()
host_port = random.choice(option.status_backend_port_range) 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}" url = f"http://127.0.0.1:{host_port}"
option.status_backend_port_range.remove(host_port) option.status_backend_port_range.remove(host_port)
@ -40,6 +44,7 @@ class StatusBackend(RpcClient, SignalClient):
self.api_url = f"{url}/statusgo" self.api_url = f"{url}/statusgo"
self.ws_url = f"{url}".replace("http", "ws") self.ws_url = f"{url}".replace("http", "ws")
self.rpc_url = f"{url}/statusgo/CallRPC" self.rpc_url = f"{url}/statusgo/CallRPC"
self.public_key = ""
RpcClient.__init__(self, self.rpc_url) RpcClient.__init__(self, self.rpc_url)
SignalClient.__init__(self, self.ws_url, await_signals) SignalClient.__init__(self, self.ws_url, await_signals)
@ -50,7 +55,12 @@ class StatusBackend(RpcClient, SignalClient):
websocket_thread.daemon = True websocket_thread.daemon = True
websocket_thread.start() 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 docker_project_name = option.docker_project_name
timestamp = int(time.time() * 1000) # Keep in sync with run_functional_tests.sh timestamp = int(time.time() * 1000) # Keep in sync with run_functional_tests.sh
@ -62,6 +72,7 @@ class StatusBackend(RpcClient, SignalClient):
container_args = { container_args = {
"image": image_name, "image": image_name,
"detach": True, "detach": True,
"privileged": privileged,
"name": container_name, "name": container_name,
"labels": {"com.docker.compose.project": docker_project_name}, "labels": {"com.docker.compose.project": docker_project_name},
"entrypoint": [ "entrypoint": [
@ -182,21 +193,26 @@ class StatusBackend(RpcClient, SignalClient):
def create_account_and_login( def create_account_and_login(
self, self,
data_dir=USER_DIR, data_dir=USER_DIR,
display_name=DEFAULT_DISPLAY_NAME, display_name=None,
password=user_1.password, 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" method = "CreateAccountAndLogin"
data = { data = {
"rootDataDir": data_dir, "rootDataDir": data_dir,
"kdfIterations": 256000, "kdfIterations": 256000,
"displayName": display_name, "displayName": self.display_name,
"password": password, "password": password,
"customizationColor": "primary", "customizationColor": "primary",
"logEnabled": True, "logEnabled": True,
"logLevel": "DEBUG", "logLevel": "DEBUG",
} }
data = self._set_proxy_credentials(data) 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( def restore_account_and_login(
self, self,
@ -256,72 +272,34 @@ class StatusBackend(RpcClient, SignalClient):
# ToDo: change this part for waiting for `node.login` signal when websockets are migrated to StatusBackend # ToDo: change this part for waiting for `node.login` signal when websockets are migrated to StatusBackend
while time.time() - start_time <= timeout: while time.time() - start_time <= timeout:
try: try:
self.rpc_valid_request(method="accounts_getKeypairs") self.accounts_service.get_account_keypairs()
return return
except AssertionError: except AssertionError:
time.sleep(3) time.sleep(3)
raise TimeoutError(f"RPC client was not started after {timeout} seconds") 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 container_pause(self):
def start_messenger(self, params=[]): if not self.container:
method = "wakuext_startMessenger" raise RuntimeError("Container is not initialized.")
response = self.rpc_request(method, params) self.container.pause()
json_response = response.json() logging.info(f"Container {self.container.name} paused.")
if "error" in json_response: def container_unpause(self):
assert json_response["error"]["code"] == -32000 if not self.container:
assert json_response["error"]["message"] == "messenger already started" raise RuntimeError("Container is not initialized.")
return self.container.unpause()
logging.info(f"Container {self.container.name} unpaused.")
self.verify_is_valid_json_rpc_response(response) 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 start_wallet(self, params=[]): def find_public_key(self):
method = "wallet_startWallet" self.public_key = self.node_login_event.get("event", {}).get("settings", {}).get("public-key")
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()

View File

@ -1,38 +1,28 @@
from time import sleep from time import sleep
from uuid import uuid4 from uuid import uuid4
import pytest import pytest
from test_cases import OneToOneMessageTestCase from test_cases import MessengerTestCase
from resources.constants import DEFAULT_DISPLAY_NAME
from clients.signals import SignalType from clients.signals import SignalType
from resources.enums import MessageContentType from resources.enums import MessageContentType
@pytest.mark.rpc @pytest.mark.reliability
class TestContactRequests(OneToOneMessageTestCase): 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") @pytest.mark.dependency(name="test_contact_request_baseline")
def test_contact_request_baseline(self, execution_number=1): 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()}" 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) existing_contacts = receiver.wakuext_service.get_contacts()
receiver = self.initialize_backend(await_signals=await_signals)
pk_sender = sender.get_pubkey(DEFAULT_DISPLAY_NAME) if sender.public_key in str(existing_contacts):
pk_receiver = receiver.get_pubkey(DEFAULT_DISPLAY_NAME)
existing_contacts = receiver.get_contacts()
if pk_sender in str(existing_contacts):
pytest.skip("Contact request was already sent for this sender<->receiver. Skipping test!!") pytest.skip("Contact request was already sent for this sender<->receiver. Skipping test!!")
response = 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) expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.CONTACT_REQUEST.value)[0]
messages_new_event = receiver.find_signal_containing_pattern( messages_new_event = receiver.find_signal_containing_pattern(
SignalType.MESSAGES_NEW.value, SignalType.MESSAGES_NEW.value,
@ -44,7 +34,9 @@ class TestContactRequests(OneToOneMessageTestCase):
if "messages" in messages_new_event.get("event", {}): 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) 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( self.validate_signal_event_against_response(
signal_event=messages_new_event, signal_event=messages_new_event,
@ -67,23 +59,28 @@ class TestContactRequests(OneToOneMessageTestCase):
@pytest.mark.dependency(depends=["test_contact_request_baseline"]) @pytest.mark.dependency(depends=["test_contact_request_baseline"])
@pytest.mark.skip(reason="Skipping until add_latency is implemented") @pytest.mark.skip(reason="Skipping until add_latency is implemented")
def test_contact_request_with_latency(self): def test_contact_request_with_latency(self):
with self.add_latency(): # with self.add_latency():
self.test_contact_request_baseline() # self.test_contact_request_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_contact_request_baseline"]) @pytest.mark.dependency(depends=["test_contact_request_baseline"])
@pytest.mark.skip(reason="Skipping until add_packet_loss is implemented") @pytest.mark.skip(reason="Skipping until add_packet_loss is implemented")
def test_contact_request_with_packet_loss(self): def test_contact_request_with_packet_loss(self):
with self.add_packet_loss(): # with self.add_packet_loss():
self.test_contact_request_baseline() # self.test_contact_request_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_contact_request_baseline"]) @pytest.mark.dependency(depends=["test_contact_request_baseline"])
@pytest.mark.skip(reason="Skipping until add_low_bandwith is implemented") @pytest.mark.skip(reason="Skipping until add_low_bandwith is implemented")
def test_contact_request_with_low_bandwidth(self): def test_contact_request_with_low_bandwidth(self):
with self.add_low_bandwith(): # with self.add_low_bandwith():
self.test_contact_request_baseline() # self.test_contact_request_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_contact_request_baseline"]) @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): def test_contact_request_with_node_pause_30_seconds(self):
await_signals = [ await_signals = [
SignalType.MESSAGES_NEW.value, SignalType.MESSAGES_NEW.value,
@ -91,11 +88,11 @@ class TestContactRequests(OneToOneMessageTestCase):
] ]
sender = self.initialize_backend(await_signals=await_signals) sender = self.initialize_backend(await_signals=await_signals)
receiver = 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): with self.node_pause(receiver):
message_text = f"test_contact_request_{uuid4()}" 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) sleep(30)
receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_text) receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=expected_message.get("id"))
sender.wait_for_signal("messages.delivered") sender.wait_for_signal(SignalType.MESSAGE_DELIVERED.value)

View File

@ -0,0 +1,77 @@
from time import sleep
from uuid import uuid4
import pytest
from test_cases import MessengerTestCase
from clients.signals import SignalType
from resources.enums import MessageContentType
@pytest.mark.usefixtures("setup_two_nodes")
@pytest.mark.reliability
class TestCreatePrivateGroups(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_create_private_group_baseline")
def test_create_private_group_baseline(self, private_groups_count=1):
self.make_contacts()
private_groups = []
for i in range(private_groups_count):
private_group_name = f"private_group_{i+1}_{uuid4()}"
response = self.sender.wakuext_service.create_group_chat_with_members([self.receiver.public_key], private_group_name)
expected_group_creation_msg = f"@{self.sender.public_key} created the group {private_group_name}"
expected_message = self.get_message_by_content_type(
response, content_type=MessageContentType.SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP.value, message_pattern=expected_group_creation_msg
)[0]
private_groups.append(expected_message)
sleep(0.01)
for i, expected_message in enumerate(private_groups):
messages_new_event = self.receiver.find_signal_containing_pattern(
SignalType.MESSAGES_NEW.value, event_pattern=expected_message.get("id"), timeout=60
)
self.validate_signal_event_against_response(
signal_event=messages_new_event,
expected_message=expected_message,
fields_to_validate={"text": "text"},
)
@pytest.mark.dependency(depends=["test_create_private_group_baseline"])
def test_multiple_one_create_private_groups(self):
self.test_create_private_group_baseline(private_groups_count=50)
@pytest.mark.dependency(depends=["test_create_private_group_baseline"])
@pytest.mark.skip(reason="Skipping until add_latency is implemented")
def test_create_private_groups_with_latency(self):
# with self.add_latency():
# self.test_create_private_group_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_create_private_group_baseline"])
@pytest.mark.skip(reason="Skipping until add_packet_loss is implemented")
def test_create_private_groups_with_packet_loss(self):
# with self.add_packet_loss():
# self.test_create_private_group_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_create_private_group_baseline"])
@pytest.mark.skip(reason="Skipping until add_low_bandwith is implemented")
def test_create_private_groups_with_low_bandwidth(self):
# with self.add_low_bandwith():
# self.test_create_private_group_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_create_private_group_baseline"])
def test_create_private_groups_with_node_pause_30_seconds(self):
self.make_contacts()
with self.node_pause(self.receiver):
private_group_name = f"private_group_{uuid4()}"
self.sender.wakuext_service.create_group_chat_with_members([self.receiver.public_key], private_group_name)
sleep(30)
self.receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=private_group_name)

View File

@ -1,35 +1,23 @@
from time import sleep from time import sleep
from uuid import uuid4 from uuid import uuid4
import pytest import pytest
from test_cases import OneToOneMessageTestCase from test_cases import MessengerTestCase
from resources.constants import DEFAULT_DISPLAY_NAME
from clients.signals import SignalType from clients.signals import SignalType
from resources.enums import MessageContentType from resources.enums import MessageContentType
@pytest.mark.rpc @pytest.mark.usefixtures("setup_two_nodes")
class TestOneToOneMessages(OneToOneMessageTestCase): @pytest.mark.reliability
class TestOneToOneMessages(MessengerTestCase):
@pytest.fixture(scope="class", autouse=True)
def setup_nodes(self, request):
await_signals = [
SignalType.MESSAGES_NEW.value,
SignalType.MESSAGE_DELIVERED.value,
]
request.cls.sender = self.sender = self.initialize_backend(await_signals=await_signals)
request.cls.receiver = self.receiver = self.initialize_backend(await_signals=await_signals)
@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_one_to_one_message_baseline") @pytest.mark.dependency(name="test_one_to_one_message_baseline")
def test_one_to_one_message_baseline(self, message_count=1): def test_one_to_one_message_baseline(self, message_count=1):
pk_receiver = self.receiver.get_pubkey(DEFAULT_DISPLAY_NAME)
self.sender.send_contact_request(pk_receiver, "contact_request")
sent_messages = [] sent_messages = []
for i in range(message_count): for i in range(message_count):
message_text = f"test_message_{i+1}_{uuid4()}" message_text = f"test_message_{i+1}_{uuid4()}"
response = self.sender.send_message(pk_receiver, message_text) response = self.sender.wakuext_service.send_message(self.receiver.public_key, message_text)
expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.TEXT_PLAIN.value) expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.TEXT_PLAIN.value)[0]
sent_messages.append(expected_message) sent_messages.append(expected_message)
sleep(0.01) sleep(0.01)
@ -52,29 +40,32 @@ class TestOneToOneMessages(OneToOneMessageTestCase):
@pytest.mark.dependency(depends=["test_one_to_one_message_baseline"]) @pytest.mark.dependency(depends=["test_one_to_one_message_baseline"])
@pytest.mark.skip(reason="Skipping until add_latency is implemented") @pytest.mark.skip(reason="Skipping until add_latency is implemented")
def test_one_to_one_message_with_latency(self): def test_one_to_one_message_with_latency(self):
with self.add_latency(): # with self.add_latency():
self.test_one_to_one_message_baseline() # self.test_one_to_one_message_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_one_to_one_message_baseline"]) @pytest.mark.dependency(depends=["test_one_to_one_message_baseline"])
@pytest.mark.skip(reason="Skipping until add_packet_loss is implemented") @pytest.mark.skip(reason="Skipping until add_packet_loss is implemented")
def test_one_to_one_message_with_packet_loss(self): def test_one_to_one_message_with_packet_loss(self):
with self.add_packet_loss(): # with self.add_packet_loss():
self.test_one_to_one_message_baseline() # self.test_one_to_one_message_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_one_to_one_message_baseline"]) @pytest.mark.dependency(depends=["test_one_to_one_message_baseline"])
@pytest.mark.skip(reason="Skipping until add_low_bandwith is implemented") @pytest.mark.skip(reason="Skipping until add_low_bandwith is implemented")
def test_one_to_one_message_with_low_bandwidth(self): def test_one_to_one_message_with_low_bandwidth(self):
with self.add_low_bandwith(): # with self.add_low_bandwith():
self.test_one_to_one_message_baseline() # self.test_one_to_one_message_baseline()
# to be done in the next PR
pass
@pytest.mark.dependency(depends=["test_one_to_one_message_baseline"]) @pytest.mark.dependency(depends=["test_one_to_one_message_baseline"])
@pytest.mark.skip(reason="Skipping until node_pause is implemented")
def test_one_to_one_message_with_node_pause_30_seconds(self): def test_one_to_one_message_with_node_pause_30_seconds(self):
pk_receiver = self.receiver.get_pubkey("Receiver")
self.sender.send_contact_request(pk_receiver, "contact_request")
with self.node_pause(self.receiver): with self.node_pause(self.receiver):
message_text = f"test_message_{uuid4()}" message_text = f"test_message_{uuid4()}"
self.sender.send_message(pk_receiver, message_text) self.sender.wakuext_service.send_message(self.receiver.public_key, message_text)
sleep(30) sleep(30)
self.receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_text) self.receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_text)
self.sender.wait_for_signal("messages.delivered") self.sender.wait_for_signal(SignalType.MESSAGE_DELIVERED.value)

View File

@ -11,7 +11,8 @@ from clients.services.wallet import WalletService
from clients.signals import SignalClient, SignalType from clients.signals import SignalClient, SignalType
from clients.status_backend import RpcClient, StatusBackend from clients.status_backend import RpcClient, StatusBackend
from conftest import option from conftest import option
from resources.constants import user_1, user_2, DEFAULT_DISPLAY_NAME from resources.constants import user_1, user_2
from resources.enums import MessageContentType
class StatusDTestCase: class StatusDTestCase:
@ -151,47 +152,80 @@ class SignalTestCase(StatusDTestCase):
class NetworkConditionTestCase: class NetworkConditionTestCase:
@contextmanager @contextmanager
def add_latency(self): def add_latency(self, node, latency=300, jitter=50):
logging.info("Entering context manager: add_latency")
node.container_exec(f"apk add iproute2 && tc qdisc add dev eth0 root netem delay {latency}ms {jitter}ms distribution normal")
try: try:
# TODO: To be implemented when we have docker exec capability
yield yield
finally: finally:
pass logging.info("Exiting context manager: add_latency")
node.container_exec("tc qdisc del dev eth0 root")
@contextmanager @contextmanager
def add_packet_loss(self): def add_packet_loss(self, node, packet_loss=2):
logging.info("Entering context manager: add_packet_loss")
node.container_exec(f"apk add iproute2 && tc qdisc add dev eth0 root netem loss {packet_loss}%")
try: try:
# TODO: To be implemented when we have docker exec capability
yield yield
finally: finally:
pass logging.info("Exiting context manager: add_packet_loss")
node.container_exec("tc qdisc del dev eth0 root netem")
@contextmanager @contextmanager
def add_low_bandwith(self): def add_low_bandwith(self, node, rate="1mbit", burst="32kbit"):
logging.info("Entering context manager: add_low_bandwith")
node.container_exec(f"apk add iproute2 && tc qdisc add dev eth0 root tbf rate {rate} burst {burst}")
try: try:
# TODO: To be implemented when we have docker exec capability
yield yield
finally: finally:
pass logging.info("Exiting context manager: add_low_bandwith")
node.container_exec("tc qdisc del dev eth0 root")
@contextmanager @contextmanager
def node_pause(self, node): def node_pause(self, node):
logging.info("Entering context manager: node_pause")
node.container_pause()
try: try:
# TODO: To be implemented when we have docker exec capability
yield yield
finally: finally:
pass logging.info("Exiting context manager: node_pause")
node.container_unpause()
class OneToOneMessageTestCase(NetworkConditionTestCase): class MessengerTestCase(NetworkConditionTestCase):
def initialize_backend(self, await_signals, display_name=DEFAULT_DISPLAY_NAME): await_signals = [
SignalType.MESSAGES_NEW.value,
SignalType.MESSAGE_DELIVERED.value,
SignalType.NODE_LOGIN.value,
]
@pytest.fixture(scope="class", autouse=False)
def setup_two_nodes(self, request):
request.cls.sender = self.sender = self.initialize_backend(await_signals=self.await_signals)
request.cls.receiver = self.receiver = self.initialize_backend(await_signals=self.await_signals)
def initialize_backend(self, await_signals):
backend = StatusBackend(await_signals=await_signals) backend = StatusBackend(await_signals=await_signals)
backend.init_status_backend() backend.init_status_backend()
backend.create_account_and_login(display_name=display_name) backend.create_account_and_login()
backend.start_messenger() backend.find_public_key()
backend.wakuext_service.start_messenger()
return backend return backend
def make_contacts(self):
existing_contacts = self.receiver.wakuext_service.get_contacts()
if self.sender.public_key in str(existing_contacts):
return
response = self.sender.wakuext_service.send_contact_request(self.receiver.public_key, "contact_request")
expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.CONTACT_REQUEST.value)[0]
self.receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=expected_message.get("id"))
self.receiver.wakuext_service.accept_contact_request(expected_message.get("id"))
accepted_signal = f"@{self.receiver.public_key} accepted your contact request"
self.sender.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=accepted_signal)
def validate_signal_event_against_response(self, signal_event, fields_to_validate, expected_message): def validate_signal_event_against_response(self, signal_event, fields_to_validate, expected_message):
expected_message_id = expected_message.get("id") expected_message_id = expected_message.get("id")
signal_event_messages = signal_event.get("event", {}).get("messages") signal_event_messages = signal_event.get("event", {}).get("messages")
@ -218,10 +252,15 @@ class OneToOneMessageTestCase(NetworkConditionTestCase):
"Details of mismatches:\n" + "\n".join(message_mismatch) "Details of mismatches:\n" + "\n".join(message_mismatch)
) )
def get_message_by_content_type(self, response, content_type): def get_message_by_content_type(self, response, content_type, message_pattern=""):
matched_messages = []
messages = response.get("result", {}).get("messages", []) messages = response.get("result", {}).get("messages", [])
for message in messages: for message in messages:
if message.get("contentType") == content_type: if message.get("contentType") != content_type:
return message continue
if not message_pattern or message_pattern in str(message):
raise ValueError(f"Failed to find a message with contentType '{content_type}' in response") matched_messages.append(message)
if matched_messages:
return matched_messages
else:
raise ValueError(f"Failed to find a message with contentType '{content_type}' in response")