From 945364b0b223d9596f1fe708dea313415f75262a Mon Sep 17 00:00:00 2001 From: fbarbu15 Date: Tue, 20 Aug 2024 14:11:36 +0300 Subject: [PATCH] e2e tests (#65) --- src/env_vars.py | 3 +- src/node/waku_node.py | 12 ++- src/steps/common.py | 11 ++- src/steps/filter.py | 5 +- src/steps/light_push.py | 3 +- src/steps/relay.py | 7 +- src/steps/rln.py | 3 +- src/steps/sharding.py | 2 - src/steps/store.py | 3 +- tests/discv5/test_discv5.py | 10 +- tests/e2e/__init__.py | 0 tests/e2e/test_e2e.py | 179 ++++++++++++++++++++++++++++++++++++ 12 files changed, 214 insertions(+), 24 deletions(-) create mode 100644 tests/e2e/__init__.py create mode 100644 tests/e2e/test_e2e.py diff --git a/src/env_vars.py b/src/env_vars.py index f58aeeb232..51b0c4d43f 100644 --- a/src/env_vars.py +++ b/src/env_vars.py @@ -16,7 +16,7 @@ def get_env_var(var_name, default=None): # Configuration constants. Need to be upercase to appear in reports DEFAULT_NWAKU = "wakuorg/nwaku:latest" DEFAULT_GOWAKU = "wakuorg/go-waku:latest" -NODE_1 = get_env_var("NODE_1", DEFAULT_GOWAKU) +NODE_1 = get_env_var("NODE_1", DEFAULT_NWAKU) NODE_2 = get_env_var("NODE_2", DEFAULT_NWAKU) ADDITIONAL_NODES = get_env_var("ADDITIONAL_NODES", f"{DEFAULT_NWAKU},{DEFAULT_GOWAKU},{DEFAULT_NWAKU}") # more nodes need to follow the NODE_X pattern @@ -26,7 +26,6 @@ SUBNET = get_env_var("SUBNET", "172.18.0.0/16") IP_RANGE = get_env_var("IP_RANGE", "172.18.0.0/24") GATEWAY = get_env_var("GATEWAY", "172.18.0.1") RUNNING_IN_CI = get_env_var("CI") -NODEKEY = get_env_var("NODEKEY", "30348dd51465150e04a5d9d932c72864c8967f806cce60b5d26afeca1e77eb68") API_REQUEST_TIMEOUT = get_env_var("API_REQUEST_TIMEOUT", 20) RLN_CREDENTIALS = get_env_var("RLN_CREDENTIALS") PG_USER = get_env_var("POSTGRES_USER", "postgres") diff --git a/src/node/waku_node.py b/src/node/waku_node.py index fac55965bd..c1f62039be 100644 --- a/src/node/waku_node.py +++ b/src/node/waku_node.py @@ -1,9 +1,9 @@ import errno import json import os +import random import shutil -import subprocess - +import string import pytest import requests from src.libs.common import delay @@ -115,6 +115,7 @@ class WakuNode: "cluster-id": DEFAULT_CLUSTER_ID, "rln-creds-id": None, "rln-creds-source": None, + "nodekey": self.generate_random_nodekey(), } if self.is_gowaku(): @@ -509,3 +510,10 @@ class WakuNode: @property def container(self): return self._container + + def generate_random_nodekey(self): + # Define the set of hexadecimal characters + hex_chars = string.hexdigits.lower() + # Generate a random 64-character string from the hex characters + random_key = "".join(random.choice(hex_chars) for _ in range(64)) + return random_key diff --git a/src/steps/common.py b/src/steps/common.py index 1ce4db9b59..dbb06bb599 100644 --- a/src/steps/common.py +++ b/src/steps/common.py @@ -5,7 +5,7 @@ from time import time import allure import pytest from tenacity import retry, stop_after_delay, wait_fixed -from src.libs.common import to_base64 +from src.libs.common import delay, to_base64 from src.libs.custom_logger import get_custom_logger logger = get_custom_logger(__name__) @@ -31,6 +31,15 @@ class StepsCommon: peer_info = {"multiaddr": multiaddr, "protocols": ["/vac/waku/relay/2.0.0"], "shards": shards} node.add_peers(peer_info) + @allure.step + @retry(stop=stop_after_delay(70), wait=wait_fixed(1), reraise=True) + def wait_for_autoconnection(self, node_list, hard_wait=None): + for node in node_list: + get_peers = node.get_peers() + assert len(get_peers) >= 1 + if hard_wait: + delay(hard_wait) + @allure.step def create_message(self, **kwargs): message = {"payload": to_base64(self.test_payload), "contentTopic": self.test_content_topic, "timestamp": int(time() * 1e9)} diff --git a/src/steps/filter.py b/src/steps/filter.py index 7a4ea9a120..1d4774a07f 100644 --- a/src/steps/filter.py +++ b/src/steps/filter.py @@ -6,7 +6,7 @@ import pytest import allure from src.libs.common import to_base64, delay from src.node.waku_message import WakuMessage -from src.env_vars import NODE_1, NODE_2, ADDITIONAL_NODES, NODEKEY +from src.env_vars import NODE_1, NODE_2, ADDITIONAL_NODES from src.node.waku_node import WakuNode from tenacity import retry, stop_after_delay, wait_fixed from src.steps.common import StepsCommon @@ -60,12 +60,13 @@ class StepsFilter(StepsCommon): def relay_node_start(self, node): self.node1 = WakuNode(node, f"node1_{self.test_id}") - start_args = {"relay": "true", "filter": "true", "nodekey": NODEKEY} + start_args = {"relay": "true", "filter": "true"} if self.node1.is_gowaku(): start_args["min_relay_peers_to_publish"] = "0" self.node1.start(**start_args) self.enr_uri = self.node1.get_enr_uri() self.multiaddr_with_id = self.node1.get_multiaddr_with_id() + return self.node1 def setup_optional_filter_nodes(self, node_list=ADDITIONAL_NODES): if node_list: diff --git a/src/steps/light_push.py b/src/steps/light_push.py index d4f41f71ca..4fd8d929e2 100644 --- a/src/steps/light_push.py +++ b/src/steps/light_push.py @@ -9,7 +9,6 @@ from src.env_vars import ( ADDITIONAL_NODES, NODE_1, NODE_2, - NODEKEY, ) from src.node.waku_node import WakuNode from src.steps.common import StepsCommon @@ -51,7 +50,7 @@ class StepsLightPush(StepsCommon): @allure.step def setup_first_receiving_node(self, lightpush="true", relay="true", **kwargs): - self.receiving_node1 = self.start_receiving_node(NODE_1, node_index=1, lightpush=lightpush, relay=relay, nodekey=NODEKEY, **kwargs) + self.receiving_node1 = self.start_receiving_node(NODE_1, node_index=1, lightpush=lightpush, relay=relay, **kwargs) self.enr_uri = self.receiving_node1.get_enr_uri() @allure.step diff --git a/src/steps/relay.py b/src/steps/relay.py index 5229b4842b..840461bbe7 100644 --- a/src/steps/relay.py +++ b/src/steps/relay.py @@ -9,7 +9,6 @@ from src.env_vars import ( NODE_1, NODE_2, ADDITIONAL_NODES, - NODEKEY, ) from src.node.waku_node import WakuNode from tenacity import retry, stop_after_delay, wait_fixed @@ -34,7 +33,7 @@ class StepsRelay(StepsCommon): def setup_main_relay_nodes(self, request): logger.debug(f"Running fixture setup: {inspect.currentframe().f_code.co_name}") self.node1 = WakuNode(NODE_1, f"node1_{request.cls.test_id}") - self.node1.start(relay="true", nodekey=NODEKEY) + self.node1.start(relay="true") self.enr_uri = self.node1.get_enr_uri() self.multiaddr_with_id = self.node1.get_multiaddr_with_id() self.node2 = WakuNode(NODE_2, f"node2_{request.cls.test_id}") @@ -77,7 +76,7 @@ class StepsRelay(StepsCommon): @allure.step def setup_first_relay_node(self, **kwargs): self.node1 = WakuNode(NODE_1, f"node1_{self.test_id}") - self.node1.start(relay="true", nodekey=NODEKEY, **kwargs) + self.node1.start(relay="true", **kwargs) self.enr_uri = self.node1.get_enr_uri() self.multiaddr_with_id = self.node1.get_multiaddr_with_id() self.main_nodes.extend([self.node1]) @@ -167,7 +166,7 @@ class StepsRelay(StepsCommon): @allure.step def setup_main_nodes(self, **kwargs): self.node1 = WakuNode(NODE_1, f"node1_{self.test_id}") - self.node1.start(relay="true", nodekey=NODEKEY, **kwargs) + self.node1.start(relay="true", **kwargs) self.enr_uri = self.node1.get_enr_uri() self.multiaddr_with_id = self.node1.get_multiaddr_with_id() self.node2 = WakuNode(NODE_2, f"node2_{self.test_id}") diff --git a/src/steps/rln.py b/src/steps/rln.py index 2300804010..e4f0e6e606 100644 --- a/src/steps/rln.py +++ b/src/steps/rln.py @@ -8,7 +8,7 @@ import allure from src.steps.common import StepsCommon from src.test_data import PUBSUB_TOPICS_RLN -from src.env_vars import DEFAULT_NWAKU, RLN_CREDENTIALS, NODEKEY, NODE_1, NODE_2, ADDITIONAL_NODES +from src.env_vars import DEFAULT_NWAKU, RLN_CREDENTIALS, NODE_1, NODE_2, ADDITIONAL_NODES from src.libs.common import gen_step_id, delay from src.libs.custom_logger import get_custom_logger from src.node.waku_node import WakuNode, rln_credential_store_ready @@ -56,7 +56,6 @@ class StepsRLN(StepsCommon): self.node1 = WakuNode(NODE_1, f"node1_{self.test_id}") self.node1.start( relay="true", - nodekey=NODEKEY, rln_creds_source=RLN_CREDENTIALS, rln_creds_id="1", rln_relay_membership_index="1", diff --git a/src/steps/sharding.py b/src/steps/sharding.py index fb3d828f24..d488819fc1 100644 --- a/src/steps/sharding.py +++ b/src/steps/sharding.py @@ -8,10 +8,8 @@ from src.libs.common import to_base64, delay from src.node.waku_message import WakuMessage from src.env_vars import ( DEFAULT_NWAKU, - NODE_1, NODE_2, ADDITIONAL_NODES, - NODEKEY, ) from src.node.waku_node import WakuNode from src.steps.common import StepsCommon diff --git a/src/steps/store.py b/src/steps/store.py index ad44243598..bebc3b6426 100644 --- a/src/steps/store.py +++ b/src/steps/store.py @@ -9,7 +9,6 @@ from src.env_vars import ( ADDITIONAL_NODES, NODE_1, NODE_2, - NODEKEY, ) from src.node.waku_node import WakuNode from src.steps.common import StepsCommon @@ -62,7 +61,7 @@ class StepsStore(StepsCommon): @allure.step def setup_first_publishing_node(self, store="true", relay="true", **kwargs): - self.publishing_node1 = self.start_publishing_node(NODE_1, node_index=1, store=store, relay=relay, nodekey=NODEKEY, **kwargs) + self.publishing_node1 = self.start_publishing_node(NODE_1, node_index=1, store=store, relay=relay, **kwargs) self.enr_uri = self.publishing_node1.get_enr_uri() @allure.step diff --git a/tests/discv5/test_discv5.py b/tests/discv5/test_discv5.py index eedfba70ec..2557ea90c1 100644 --- a/tests/discv5/test_discv5.py +++ b/tests/discv5/test_discv5.py @@ -1,4 +1,4 @@ -from src.env_vars import NODE_1, NODE_2, NODEKEY +from src.env_vars import NODE_1, NODE_2 from src.libs.custom_logger import get_custom_logger from src.node.waku_node import WakuNode from src.steps.filter import StepsFilter @@ -26,14 +26,14 @@ class TestDiscv5(StepsRelay, StepsFilter, StepsStore, StepsLightPush): self.check_light_pushed_message_reaches_receiving_peer(peer_list=[self.receiving_node1, self.receiving_node2]) def test_relay(self): - self.node1 = self.running_a_node(NODE_1, relay="true", nodekey=NODEKEY) + self.node1 = self.running_a_node(NODE_1, relay="true") self.node2 = self.running_a_node(NODE_2, relay="true", discv5_bootstrap_node=self.node1.get_enr_uri()) self.main_nodes = [self.node1, self.node2] self.ensure_relay_subscriptions_on_nodes(self.main_nodes, [self.test_pubsub_topic]) self.wait_for_published_message_to_reach_relay_peer() def test_filter(self): - self.node1 = self.running_a_node(NODE_1, relay="true", filter="true", nodekey=NODEKEY) + self.node1 = self.running_a_node(NODE_1, relay="true", filter="true") self.node2 = self.running_a_node( NODE_2, relay="false", discv5_bootstrap_node=self.node1.get_enr_uri(), filternode=self.node1.get_multiaddr_with_id() ) @@ -42,7 +42,7 @@ class TestDiscv5(StepsRelay, StepsFilter, StepsStore, StepsLightPush): self.check_published_message_reaches_filter_peer() def test_store(self): - self.publishing_node1 = self.running_a_node(NODE_1, relay="true", store="true", nodekey=NODEKEY) + self.publishing_node1 = self.running_a_node(NODE_1, relay="true", store="true") self.store_node1 = self.running_a_node( NODE_2, relay="true", @@ -55,7 +55,7 @@ class TestDiscv5(StepsRelay, StepsFilter, StepsStore, StepsLightPush): self.wait_for_published_message_to_be_stored() def test_lightpush(self): - self.receiving_node1 = self.running_a_node(NODE_1, lightpush="true", relay="true", nodekey=NODEKEY) + self.receiving_node1 = self.running_a_node(NODE_1, lightpush="true", relay="true") self.receiving_node2 = self.running_a_node(NODE_1, lightpush="false", relay="true", discv5_bootstrap_node=self.receiving_node1.get_enr_uri()) self.light_push_node1 = self.running_a_node( NODE_2, diff --git a/tests/e2e/__init__.py b/tests/e2e/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/e2e/test_e2e.py b/tests/e2e/test_e2e.py new file mode 100644 index 0000000000..0c5eee3ad0 --- /dev/null +++ b/tests/e2e/test_e2e.py @@ -0,0 +1,179 @@ +import pytest +from src.env_vars import NODE_1, NODE_2 +from src.libs.common import delay +from src.libs.custom_logger import get_custom_logger +from src.node.waku_node import WakuNode +from src.steps.filter import StepsFilter +from src.steps.light_push import StepsLightPush +from src.steps.relay import StepsRelay +from src.steps.store import StepsStore + +logger = get_custom_logger(__name__) + +""" +In those tests we aim to combine multiple protocols/node types and create a more end-to-end scenario +""" + + +class TestE2E(StepsFilter, StepsStore, StepsRelay, StepsLightPush): + @pytest.fixture(scope="function", autouse=True) + def nodes(self): + self.node1 = WakuNode(NODE_2, f"node1_{self.test_id}") + self.node2 = WakuNode(NODE_1, f"node2_{self.test_id}") + self.node3 = WakuNode(NODE_2, f"node3_{self.test_id}") + + def test_relay_receiving_node_not_connected_directly_to_relaying_node(self): + self.node1.start(relay="true") + self.node2.start(relay="true", discv5_bootstrap_node=self.node1.get_enr_uri()) + self.node3.start(relay="true", discv5_bootstrap_node=self.node2.get_enr_uri()) + + self.node1.set_relay_subscriptions([self.test_pubsub_topic]) + self.node2.set_relay_subscriptions([self.test_pubsub_topic]) + self.node3.set_relay_subscriptions([self.test_pubsub_topic]) + + # for e2e scenarios I think waiting for autoconnection is important instead of forcing connection via API calls + # that's why the bellow code is commented to showcase this difference. In case we uncomment it, the connection will be faster + # self.add_node_peer(self.node2, [self.node1.get_multiaddr_with_id()]) + # self.add_node_peer(self.node3, [self.node2.get_multiaddr_with_id()]) + self.wait_for_autoconnection([self.node1, self.node2, self.node3], hard_wait=30) + + # self.node1 relays and we check that self.node3 receives the message + self.check_published_message_reaches_relay_peer(sender=self.node1, peer_list=[self.node3], message_propagation_delay=1) + + def test_filter_node_not_connected_directly_to_relaying_node(self): + self.node1.start(filter="true", relay="true") + self.node2.start(filter="true", relay="true", discv5_bootstrap_node=self.node1.get_enr_uri()) + self.node3.start(relay="false", filternode=self.node2.get_multiaddr_with_id(), discv5_bootstrap_node=self.node2.get_enr_uri()) + + self.node1.set_relay_subscriptions([self.test_pubsub_topic]) + self.node2.set_relay_subscriptions([self.test_pubsub_topic]) + self.node3.set_filter_subscriptions({"requestId": "1", "contentFilters": [self.test_content_topic], "pubsubTopic": self.test_pubsub_topic}) + + # for e2e scenarios I think waiting for autoconnection is important instead of forcing connection via API calls + # that's why the bellow code is commented to showcase this difference. In case we uncomment it, the connection will be faster + # self.add_node_peer(self.node2, [self.node1.get_multiaddr_with_id()]) + # self.add_node_peer(self.node3, [self.node2.get_multiaddr_with_id()]) + self.wait_for_autoconnection([self.node1, self.node2, self.node3], hard_wait=30) + + # self.node1 relays and we check that self.node3 receives the message + self.check_published_message_reaches_filter_peer(sender=self.node1, peer_list=[self.node3], message_propagation_delay=1) + + def test_store_node_not_connected_directly_to_relaying_node(self): + self.node1.start(relay="true") + self.node2.start(store="true", relay="true", discv5_bootstrap_node=self.node1.get_enr_uri()) + self.node3.start(relay="false", storenode=self.node2.get_multiaddr_with_id(), discv5_bootstrap_node=self.node2.get_enr_uri()) + + self.node1.set_relay_subscriptions([self.test_pubsub_topic]) + self.node2.set_relay_subscriptions([self.test_pubsub_topic]) + # self.node1 relays and we check that self.node3 receives the message + + # for e2e scenarios I think waiting for autoconnection is important instead of forcing connection via API calls + # that's why the bellow code is commented to showcase this difference. In case we uncomment it, the connection will be faster + # self.add_node_peer(self.node2, [self.node1.get_multiaddr_with_id()]) + # self.add_node_peer(self.node3, [self.node2.get_multiaddr_with_id()]) + self.wait_for_autoconnection([self.node1, self.node2], hard_wait=30) + + self.publish_message(sender=self.node1) + self.check_published_message_is_stored(page_size=50, ascending="true", store_node=self.node3) + + def test_relay_receiving_node_not_connected_directly_to_lightpushing_node(self): + self.node1.start(lightpush="true", relay="true") + self.node2.start( + lightpush="true", relay="true", discv5_bootstrap_node=self.node1.get_enr_uri(), lightpushnode=self.node1.get_multiaddr_with_id() + ) + self.node3.start(relay="true", discv5_bootstrap_node=self.node2.get_enr_uri()) + + self.node1.set_relay_subscriptions([self.test_pubsub_topic]) + self.node2.set_relay_subscriptions([self.test_pubsub_topic]) + self.node3.set_relay_subscriptions([self.test_pubsub_topic]) + + # for e2e scenarios I think waiting for autoconnection is important instead of forcing connection via API calls + # that's why the bellow code is commented to showcase this difference. In case we uncomment it, the connection will be faster + # self.add_node_peer(self.node2, [self.node1.get_multiaddr_with_id()]) + # self.add_node_peer(self.node3, [self.node2.get_multiaddr_with_id()]) + self.wait_for_autoconnection([self.node1, self.node2, self.node3], hard_wait=30) + + # self.node1 light pushed and we check that self.node3 receives the message + self.check_light_pushed_message_reaches_receiving_peer(sender=self.node1, peer_list=[self.node3]) + + def test_filter_node_not_connected_directly_to_lightpushing_node(self): + self.node1.start(lightpush="true") + self.node2.start( + lightpush="true", + filter="true", + relay="true", + discv5_bootstrap_node=self.node1.get_enr_uri(), + lightpushnode=self.node1.get_multiaddr_with_id(), + ) + self.node3.start(relay="false", filternode=self.node2.get_multiaddr_with_id(), discv5_bootstrap_node=self.node2.get_enr_uri()) + + self.node1.set_relay_subscriptions([self.test_pubsub_topic]) + self.node2.set_relay_subscriptions([self.test_pubsub_topic]) + self.node3.set_filter_subscriptions({"requestId": "1", "contentFilters": [self.test_content_topic], "pubsubTopic": self.test_pubsub_topic}) + + # for e2e scenarios I think waiting for autoconnection is important instead of forcing connection via API calls + # that's why the bellow code is commented to showcase this difference. In case we uncomment it, the connection will be faster + # self.add_node_peer(self.node2, [self.node1.get_multiaddr_with_id()]) + # self.add_node_peer(self.node3, [self.node2.get_multiaddr_with_id()]) + self.wait_for_autoconnection([self.node1, self.node2, self.node3], hard_wait=30) + + # self.node1 light pushed and we check that self.node3 receives the message + self.node1.send_light_push_message(self.create_payload()) + delay(1) + get_messages_response = self.get_filter_messages(self.test_content_topic, pubsub_topic=self.test_pubsub_topic, node=self.node3) + assert len(get_messages_response) == 1, f"Expected 1 message but got {len(get_messages_response)}" + + def test_store_node_not_connected_directly_to_lightpushing_node(self): + self.node1.start(lightpush="true") + self.node2.start( + lightpush="true", + store="true", + relay="true", + discv5_bootstrap_node=self.node1.get_enr_uri(), + lightpushnode=self.node1.get_multiaddr_with_id(), + ) + self.node3.start(relay="false", storenode=self.node2.get_multiaddr_with_id(), discv5_bootstrap_node=self.node2.get_enr_uri()) + + self.node1.set_relay_subscriptions([self.test_pubsub_topic]) + self.node2.set_relay_subscriptions([self.test_pubsub_topic]) + + # for e2e scenarios I think waiting for autoconnection is important instead of forcing connection via API calls + # that's why the bellow code is commented to showcase this difference. In case we uncomment it, the connection will be faster + # self.add_node_peer(self.node2, [self.node1.get_multiaddr_with_id()]) + # self.add_node_peer(self.node3, [self.node2.get_multiaddr_with_id()]) + self.wait_for_autoconnection([self.node1, self.node2], hard_wait=30) + + # self.node1 light pushed and we check that self.node3 receives the message + message = self.create_message() + self.node1.send_light_push_message(self.create_payload(message=message)) + delay(1) + self.check_published_message_is_stored(page_size=50, ascending="true", store_node=self.node3, message_to_check=message) + + def test_chain_of_relay_nodes(self): + self.node4 = WakuNode(NODE_2, f"node4_{self.test_id}") + self.node5 = WakuNode(NODE_2, f"node5_{self.test_id}") + self.node6 = WakuNode(NODE_2, f"node6_{self.test_id}") + self.node7 = WakuNode(NODE_2, f"node7_{self.test_id}") + self.node8 = WakuNode(NODE_2, f"node8_{self.test_id}") + self.node9 = WakuNode(NODE_2, f"node9_{self.test_id}") + self.node10 = WakuNode(NODE_2, f"node10_{self.test_id}") + + self.node1.start(relay="true") + self.node2.start(relay="true", discv5_bootstrap_node=self.node1.get_enr_uri()) + self.node3.start(relay="true", discv5_bootstrap_node=self.node2.get_enr_uri()) + self.node4.start(relay="true", discv5_bootstrap_node=self.node3.get_enr_uri()) + self.node5.start(relay="true", discv5_bootstrap_node=self.node4.get_enr_uri()) + self.node6.start(relay="true", discv5_bootstrap_node=self.node5.get_enr_uri()) + self.node7.start(relay="true", discv5_bootstrap_node=self.node6.get_enr_uri()) + self.node8.start(relay="true", discv5_bootstrap_node=self.node7.get_enr_uri()) + self.node9.start(relay="true", discv5_bootstrap_node=self.node8.get_enr_uri()) + self.node10.start(relay="true", discv5_bootstrap_node=self.node9.get_enr_uri()) + + node_list = [self.node1, self.node2, self.node3, self.node4, self.node5, self.node6, self.node7, self.node8, self.node9, self.node10] + for node in node_list: + node.set_relay_subscriptions([self.test_pubsub_topic]) + + self.wait_for_autoconnection(node_list, hard_wait=30) + + # self.node1 relays and we check that self.node10 receives the message + self.check_published_message_reaches_relay_peer(sender=self.node1, peer_list=[self.node10], message_propagation_delay=1)