diff --git a/tests-functional/clients/status_backend.py b/tests-functional/clients/status_backend.py index 0d69c6096..b0481c7ee 100644 --- a/tests-functional/clients/status_backend.py +++ b/tests-functional/clients/status_backend.py @@ -242,14 +242,29 @@ class StatusBackend(RpcClient, SignalClient): raise ValueError( f"Public key not found for display name: {display_name}") - def send_contact_request(self, params=[]): + 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 send_message(self, params=[]): - method = "wakuext_sendOneToOneMessage" + 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() diff --git a/tests-functional/resources/__init__.py b/tests-functional/resources/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests-functional/resources/enums.py b/tests-functional/resources/enums.py new file mode 100644 index 000000000..2c6944dd6 --- /dev/null +++ b/tests-functional/resources/enums.py @@ -0,0 +1,22 @@ +from enum import Enum + +class MessageContentType(Enum): + UNKNOWN_CONTENT_TYPE = 0 + TEXT_PLAIN = 1 + STICKER = 2 + STATUS = 3 + EMOJI = 4 + TRANSACTION_COMMAND = 5 + SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP = 6 + IMAGE = 7 + AUDIO = 8 + COMMUNITY = 9 + SYSTEM_MESSAGE_GAP = 10 + CONTACT_REQUEST = 11 + DISCORD_MESSAGE = 12 + IDENTITY_VERIFICATION = 13 + SYSTEM_MESSAGE_PINNED_MESSAGE = 14 + SYSTEM_MESSAGE_MUTUAL_EVENT_SENT = 15 + SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED = 16 + SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED = 17 + BRIDGE_MESSAGE = 18 diff --git a/tests-functional/tests/test_cases.py b/tests-functional/tests/test_cases.py index c082e6b14..57710eb0e 100644 --- a/tests-functional/tests/test_cases.py +++ b/tests-functional/tests/test_cases.py @@ -187,18 +187,17 @@ class OneToOneMessageTestCase(NetworkConditionTestCase): return backend - def validate_event_against_response(self, event, fields_to_validate, response): - messages_in_event = event["event"]["messages"] - assert len(messages_in_event) > 0, "No messages found in the event" - response_chat = response["result"]["chats"][0] + def validate_signal_event_against_response(self, signal_event, fields_to_validate, expected_message): + expected_message_id = expected_message.get("id") + signal_event_messages = signal_event.get("event", {}).get("messages") + assert len(signal_event_messages) > 0, "No messages found in the signal event" - message_id = response_chat["lastMessage"]["id"] - message = next((message for message in messages_in_event if message["id"] == message_id), None) - assert message, f"Message with ID {message_id} not found in the event" + message = next((message for message in signal_event_messages if message.get("id") == expected_message_id), None) + assert message, f"Message with ID {expected_message_id} not found in the signal event" message_mismatch = [] for response_field, event_field in fields_to_validate.items(): - response_value = response_chat["lastMessage"][response_field] + response_value = expected_message[response_field] event_value = message[event_field] if response_value != event_value: message_mismatch.append( @@ -213,3 +212,11 @@ class OneToOneMessageTestCase(NetworkConditionTestCase): "Details of mismatches:\n" + "\n".join(message_mismatch) ) + + def get_message_by_content_type(self, response, content_type): + messages = response.get("result", {}).get("messages", []) + for message in messages: + if message.get("contentType") == content_type: + return message + + raise ValueError(f"Failed to find a message with contentType '{content_type}' in response") diff --git a/tests-functional/tests/test_contact_request.py b/tests-functional/tests/test_contact_request.py new file mode 100644 index 000000000..c3e8b0cbd --- /dev/null +++ b/tests-functional/tests/test_contact_request.py @@ -0,0 +1,95 @@ +from time import sleep +from uuid import uuid4 +import pytest +from test_cases import OneToOneMessageTestCase +from constants import DEFAULT_DISPLAY_NAME +from clients.signals import SignalType +from resources.enums import MessageContentType + +@pytest.mark.rpc +class TestContactRequests(OneToOneMessageTestCase): + + + @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=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() + + if pk_sender 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) + + messages_new_event = receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=expected_message.get("id"), timeout=60) + + signal_messages_texts = [] + 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" + + self.validate_signal_event_against_response( + signal_event=messages_new_event, + fields_to_validate={"text": "text"}, + expected_message=expected_message + ) + + @pytest.mark.skip(reason="Skipping because of error 'Not enough status-backend containers, please add more'. Unkipping when we merge https://github.com/status-im/status-go/pull/6159") + @pytest.mark.parametrize("execution_number", range(10)) + @pytest.mark.dependency(depends=["test_contact_request_baseline"]) + def test_multiple_contact_requests(self, execution_number): + self.test_contact_request_baseline(execution_number=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() + + @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() + + @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() + + @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) + sleep(30) + receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_text) + sender.wait_for_signal("messages.delivered") diff --git a/tests-functional/tests/test_one_to_one_messages.py b/tests-functional/tests/test_one_to_one_messages.py index d9c944b16..9790cfe8f 100644 --- a/tests-functional/tests/test_one_to_one_messages.py +++ b/tests-functional/tests/test_one_to_one_messages.py @@ -4,6 +4,7 @@ import pytest from test_cases import OneToOneMessageTestCase from constants import DEFAULT_DISPLAY_NAME from clients.signals import SignalType +from resources.enums import MessageContentType @pytest.mark.rpc class TestOneToOneMessages(OneToOneMessageTestCase): @@ -21,21 +22,22 @@ class TestOneToOneMessages(OneToOneMessageTestCase): 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([{"id": pk_receiver, "message": "contact_request"}]) + self.sender.send_contact_request(pk_receiver, "contact_request") sent_messages = [] for i in range(message_count): message_text = f"test_message_{i+1}_{uuid4()}" - response = self.sender.send_message([{"id": pk_receiver, "message": message_text}]) - sent_messages.append((message_text, response)) + response = self.sender.send_message(pk_receiver, message_text) + expected_message = self.get_message_by_content_type(response, content_type=MessageContentType.TEXT_PLAIN.value) + sent_messages.append(expected_message) sleep(0.01) - for i, (message_text, response) in enumerate(sent_messages): - messages_new_event = self.receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_text, timeout=60) - self.validate_event_against_response( - messages_new_event, + for i, expected_message in enumerate(sent_messages): + 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, fields_to_validate={"text": "text"}, - response=response, + expected_message=expected_message ) @pytest.mark.dependency(depends=["test_one_to_one_message_baseline"]) @@ -64,10 +66,10 @@ class TestOneToOneMessages(OneToOneMessageTestCase): @pytest.mark.skip(reason="Skipping until node_pause is implemented") def test_one_to_one_message_with_node_pause_30_seconds(self): pk_receiver = self.receiver.get_pubkey("Receiver") - self.sender.send_contact_request([{"id": pk_receiver, "message": "contact_request"}]) + self.sender.send_contact_request(pk_receiver, "contact_request") with self.node_pause(self.receiver): message_text = f"test_message_{uuid4()}" - self.sender.send_message([{"id": pk_receiver, "message": message_text}]) + self.sender.send_message(pk_receiver, message_text) sleep(30) self.receiver.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_text) self.sender.wait_for_signal("messages.delivered")