Skip to content

Commit 810468a

Browse files
authored
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
1 parent 0cf556b commit 810468a

File tree

10 files changed

+308
-157
lines changed

10 files changed

+308
-157
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from clients.rpc import RpcClient
2+
from clients.services.service import Service
3+
4+
5+
class AccountService(Service):
6+
def __init__(self, client: RpcClient):
7+
super().__init__(client, "accounts")
8+
9+
def get_accounts(self):
10+
response = self.rpc_request("getAccounts")
11+
return response.json()
12+
13+
def get_account_keypairs(self):
14+
response = self.rpc_request("getKeypairs")
15+
return response.json()

tests-functional/clients/services/service.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ def __init__(self, client: RpcClient, name: str):
99

1010
def rpc_request(self, method: str, params=None):
1111
full_method_name = f"{self.name}_{method}"
12-
return self.rpc_client.rpc_request(full_method_name, params)
12+
return self.rpc_client.rpc_valid_request(full_method_name, params)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from clients.rpc import RpcClient
2+
from clients.services.service import Service
3+
4+
5+
class SettingsService(Service):
6+
def __init__(self, client: RpcClient):
7+
super().__init__(client, "settings")
8+
9+
def get_settings(self):
10+
response = self.rpc_request("getSettings")
11+
return response.json()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from clients.rpc import RpcClient
2+
from clients.services.service import Service
3+
4+
5+
class WakuextService(Service):
6+
def __init__(self, client: RpcClient):
7+
super().__init__(client, "wakuext")
8+
9+
def send_contact_request(self, contact_id: str, message: str):
10+
params = [{"id": contact_id, "message": message}]
11+
response = self.rpc_request("sendContactRequest", params)
12+
return response.json()
13+
14+
def accept_contact_request(self, request_id: str):
15+
params = [{"id": request_id}]
16+
response = self.rpc_request("acceptContactRequest", params)
17+
return response.json()
18+
19+
def get_contacts(self):
20+
response = self.rpc_request("contacts")
21+
return response.json()
22+
23+
def send_message(self, contact_id: str, message: str):
24+
params = [{"id": contact_id, "message": message}]
25+
response = self.rpc_request("sendOneToOneMessage", params)
26+
return response.json()
27+
28+
def start_messenger(self):
29+
response = self.rpc_request("startMessenger")
30+
json_response = response.json()
31+
32+
if "error" in json_response:
33+
assert json_response["error"]["code"] == -32000
34+
assert json_response["error"]["message"] == "messenger already started"
35+
return
36+
37+
def create_group_chat_with_members(self, pubkey_list: list, group_chat_name: str):
38+
params = [None, group_chat_name, pubkey_list]
39+
response = self.rpc_request("createGroupChatWithMembers", params)
40+
return response.json()

tests-functional/clients/services/wallet.py

+3
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ def __init__(self, client: RpcClient):
99
def get_balances_at_by_chain(self, chains: list, addresses: list, tokens: list):
1010
params = [chains, addresses, tokens]
1111
return self.rpc_request("getBalancesByChain", params)
12+
13+
def start_wallet(self):
14+
return self.rpc_request("startWallet")

tests-functional/clients/status_backend.py

+51-73
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import io
22
import json
33
import logging
4+
import string
45
import tarfile
56
import tempfile
67
import time
@@ -10,12 +11,15 @@
1011
import docker
1112
import docker.errors
1213
import os
13-
14-
from tenacity import retry, stop_after_delay, wait_fixed
15-
from clients.signals import SignalClient
14+
from clients.services.wallet import WalletService
15+
from clients.services.wakuext import WakuextService
16+
from clients.services.accounts import AccountService
17+
from clients.services.settings import SettingsService
18+
from clients.signals import SignalClient, SignalType
1619
from clients.rpc import RpcClient
1720
from conftest import option
1821
from resources.constants import user_1, DEFAULT_DISPLAY_NAME, USER_DIR
22+
from docker.errors import APIError
1923

2024
NANOSECONDS_PER_SECOND = 1_000_000_000
2125

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

2529
container = None
2630

27-
def __init__(self, await_signals=[]):
31+
def __init__(self, await_signals=[], privileged=False):
2832

2933
if option.status_backend_url:
3034
url = option.status_backend_url
3135
else:
3236
self.docker_client = docker.from_env()
3337
host_port = random.choice(option.status_backend_port_range)
3438

35-
self.container = self._start_container(host_port)
39+
self.container = self._start_container(host_port, privileged)
3640
url = f"http://127.0.0.1:{host_port}"
3741
option.status_backend_port_range.remove(host_port)
3842

3943
self.base_url = url
4044
self.api_url = f"{url}/statusgo"
4145
self.ws_url = f"{url}".replace("http", "ws")
4246
self.rpc_url = f"{url}/statusgo/CallRPC"
47+
self.public_key = ""
4348

4449
RpcClient.__init__(self, self.rpc_url)
4550
SignalClient.__init__(self, self.ws_url, await_signals)
@@ -50,7 +55,12 @@ def __init__(self, await_signals=[]):
5055
websocket_thread.daemon = True
5156
websocket_thread.start()
5257

53-
def _start_container(self, host_port):
58+
self.wallet_service = WalletService(self)
59+
self.wakuext_service = WakuextService(self)
60+
self.accounts_service = AccountService(self)
61+
self.settings_service = SettingsService(self)
62+
63+
def _start_container(self, host_port, privileged):
5464
docker_project_name = option.docker_project_name
5565

5666
timestamp = int(time.time() * 1000) # Keep in sync with run_functional_tests.sh
@@ -62,6 +72,7 @@ def _start_container(self, host_port):
6272
container_args = {
6373
"image": image_name,
6474
"detach": True,
75+
"privileged": privileged,
6576
"name": container_name,
6677
"labels": {"com.docker.compose.project": docker_project_name},
6778
"entrypoint": [
@@ -182,21 +193,26 @@ def extract_data(self, path: str):
182193
def create_account_and_login(
183194
self,
184195
data_dir=USER_DIR,
185-
display_name=DEFAULT_DISPLAY_NAME,
196+
display_name=None,
186197
password=user_1.password,
187198
):
199+
self.display_name = (
200+
display_name if display_name else f"DISP_NAME_{''.join(random.choices(string.ascii_letters + string.digits + '_-', k=10))}"
201+
)
188202
method = "CreateAccountAndLogin"
189203
data = {
190204
"rootDataDir": data_dir,
191205
"kdfIterations": 256000,
192-
"displayName": display_name,
206+
"displayName": self.display_name,
193207
"password": password,
194208
"customizationColor": "primary",
195209
"logEnabled": True,
196210
"logLevel": "DEBUG",
197211
}
198212
data = self._set_proxy_credentials(data)
199-
return self.api_valid_request(method, data)
213+
resp = self.api_valid_request(method, data)
214+
self.node_login_event = self.find_signal_containing_pattern(SignalType.NODE_LOGIN.value, event_pattern=self.display_name)
215+
return resp
200216

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

265-
@retry(stop=stop_after_delay(10), wait=wait_fixed(0.5), reraise=True)
266-
def start_messenger(self, params=[]):
267-
method = "wakuext_startMessenger"
268-
response = self.rpc_request(method, params)
269-
json_response = response.json()
270-
271-
if "error" in json_response:
272-
assert json_response["error"]["code"] == -32000
273-
assert json_response["error"]["message"] == "messenger already started"
274-
return
275-
276-
self.verify_is_valid_json_rpc_response(response)
277-
278-
def start_wallet(self, params=[]):
279-
method = "wallet_startWallet"
280-
response = self.rpc_request(method, params)
281-
self.verify_is_valid_json_rpc_response(response)
282-
283-
def get_settings(self, params=[]):
284-
method = "settings_getSettings"
285-
response = self.rpc_request(method, params)
286-
self.verify_is_valid_json_rpc_response(response)
287-
288-
def get_accounts(self, params=[]):
289-
method = "accounts_getAccounts"
290-
response = self.rpc_request(method, params)
291-
self.verify_is_valid_json_rpc_response(response)
292-
return response.json()
293-
294-
def get_pubkey(self, display_name):
295-
response = self.get_accounts()
296-
accounts = response.get("result", [])
297-
for account in accounts:
298-
if account.get("name") == display_name:
299-
return account.get("public-key")
300-
raise ValueError(f"Public key not found for display name: {display_name}")
301-
302-
def send_contact_request(self, contact_id: str, message: str):
303-
method = "wakuext_sendContactRequest"
304-
params = [{"id": contact_id, "message": message}]
305-
response = self.rpc_request(method, params)
306-
self.verify_is_valid_json_rpc_response(response)
307-
return response.json()
308-
309-
def accept_contact_request(self, chat_id: str):
310-
method = "wakuext_acceptContactRequest"
311-
params = [{"id": chat_id}]
312-
response = self.rpc_request(method, params)
313-
self.verify_is_valid_json_rpc_response(response)
314-
return response.json()
315-
316-
def get_contacts(self):
317-
method = "wakuext_contacts"
318-
response = self.rpc_request(method)
319-
self.verify_is_valid_json_rpc_response(response)
320-
return response.json()
321-
322-
def send_message(self, contact_id: str, message: str):
323-
method = "wakuext_sendOneToOneMessage"
324-
params = [{"id": contact_id, "message": message}]
325-
response = self.rpc_request(method, params)
326-
self.verify_is_valid_json_rpc_response(response)
327-
return response.json()
281+
def container_pause(self):
282+
if not self.container:
283+
raise RuntimeError("Container is not initialized.")
284+
self.container.pause()
285+
logging.info(f"Container {self.container.name} paused.")
286+
287+
def container_unpause(self):
288+
if not self.container:
289+
raise RuntimeError("Container is not initialized.")
290+
self.container.unpause()
291+
logging.info(f"Container {self.container.name} unpaused.")
292+
293+
def container_exec(self, command):
294+
if not self.container:
295+
raise RuntimeError("Container is not initialized.")
296+
try:
297+
exec_result = self.container.exec_run(cmd=["sh", "-c", command], stdout=True, stderr=True, tty=False)
298+
if exec_result.exit_code != 0:
299+
raise RuntimeError(f"Failed to execute command in container {self.container.id}:\n" f"OUTPUT: {exec_result.output.decode().strip()}")
300+
return exec_result.output.decode().strip()
301+
except APIError as e:
302+
raise RuntimeError(f"API error during container execution: {str(e)}") from e
303+
304+
def find_public_key(self):
305+
self.public_key = self.node_login_event.get("event", {}).get("settings", {}).get("public-key")
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,28 @@
11
from time import sleep
22
from uuid import uuid4
33
import pytest
4-
from test_cases import OneToOneMessageTestCase
5-
from resources.constants import DEFAULT_DISPLAY_NAME
4+
from test_cases import MessengerTestCase
65
from clients.signals import SignalType
76
from resources.enums import MessageContentType
87

98

10-
@pytest.mark.rpc
11-
class TestContactRequests(OneToOneMessageTestCase):
9+
@pytest.mark.reliability
10+
class TestContactRequests(MessengerTestCase):
1211

12+
@pytest.mark.rpc # until we have dedicated functional tests for this we can still run this test as part of the functional tests suite
1313
@pytest.mark.dependency(name="test_contact_request_baseline")
1414
def test_contact_request_baseline(self, execution_number=1):
15-
16-
await_signals = [
17-
SignalType.MESSAGES_NEW.value,
18-
SignalType.MESSAGE_DELIVERED.value,
19-
]
20-
2115
message_text = f"test_contact_request_{execution_number}_{uuid4()}"
16+
sender = self.initialize_backend(await_signals=self.await_signals)
17+
receiver = self.initialize_backend(await_signals=self.await_signals)
2218

23-
sender = self.initialize_backend(await_signals=await_signals)
24-
receiver = self.initialize_backend(await_signals=await_signals)
25-
26-
pk_sender = sender.get_pubkey(DEFAULT_DISPLAY_NAME)
27-
pk_receiver = receiver.get_pubkey(DEFAULT_DISPLAY_NAME)
28-
29-
existing_contacts = receiver.get_contacts()
19+
existing_contacts = receiver.wakuext_service.get_contacts()
3020

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

34-
response = sender.send_contact_request(pk_receiver, message_text)
35-
expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.CONTACT_REQUEST.value)
24+
response = sender.wakuext_service.send_contact_request(receiver.public_key, message_text)
25+
expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.CONTACT_REQUEST.value)[0]
3626

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

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

4941
self.validate_signal_event_against_response(
5042
signal_event=messages_new_event,
@@ -67,35 +59,40 @@ def test_multiple_contact_requests(self, execution_number):
6759
@pytest.mark.dependency(depends=["test_contact_request_baseline"])
6860
@pytest.mark.skip(reason="Skipping until add_latency is implemented")
6961
def test_contact_request_with_latency(self):
70-
with self.add_latency():
71-
self.test_contact_request_baseline()
62+
# with self.add_latency():
63+
# self.test_contact_request_baseline()
64+
# to be done in the next PR
65+
pass
7266

7367
@pytest.mark.dependency(depends=["test_contact_request_baseline"])
7468
@pytest.mark.skip(reason="Skipping until add_packet_loss is implemented")
7569
def test_contact_request_with_packet_loss(self):
76-
with self.add_packet_loss():
77-
self.test_contact_request_baseline()
70+
# with self.add_packet_loss():
71+
# self.test_contact_request_baseline()
72+
# to be done in the next PR
73+
pass
7874

7975
@pytest.mark.dependency(depends=["test_contact_request_baseline"])
8076
@pytest.mark.skip(reason="Skipping until add_low_bandwith is implemented")
8177
def test_contact_request_with_low_bandwidth(self):
82-
with self.add_low_bandwith():
83-
self.test_contact_request_baseline()
78+
# with self.add_low_bandwith():
79+
# self.test_contact_request_baseline()
80+
# to be done in the next PR
81+
pass
8482

8583
@pytest.mark.dependency(depends=["test_contact_request_baseline"])
86-
@pytest.mark.skip(reason="Skipping until node_pause is implemented")
8784
def test_contact_request_with_node_pause_30_seconds(self):
8885
await_signals = [
8986
SignalType.MESSAGES_NEW.value,
9087
SignalType.MESSAGE_DELIVERED.value,
9188
]
9289
sender = self.initialize_backend(await_signals=await_signals)
9390
receiver = self.initialize_backend(await_signals=await_signals)
94-
pk_receiver = receiver.get_pubkey(DEFAULT_DISPLAY_NAME)
9591

9692
with self.node_pause(receiver):
9793
message_text = f"test_contact_request_{uuid4()}"
98-
sender.send_contact_request(pk_receiver, message_text)
94+
response = sender.wakuext_service.send_contact_request(receiver.public_key, message_text)
95+
expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.CONTACT_REQUEST.value)[0]
9996
sleep(30)
100-
receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_text)
101-
sender.wait_for_signal("messages.delivered")
97+
receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=expected_message.get("id"))
98+
sender.wait_for_signal(SignalType.MESSAGE_DELIVERED.value)

0 commit comments

Comments
 (0)