mirror of
https://github.com/logos-messaging/logos-messaging-interop-tests.git
synced 2026-05-18 08:19:32 +00:00
* chore: build logos delivery lib locally * test: soft attachment to waku.test * chore: node1 node2 bootstrap from test fleet nodes - selected test cases relay, store * fix: cleanup artifacts after liblogosdelivery build * chore: add fleet tests workflow * fix: trigger on push and test * fix: register markers * test: add light_push to fleet tests * test: add filter to fleet tests * fix: add more store tests to fleet tests * fix: add more relay to fleet tests * fix: wf efficiency * fix: wf syntax * test: join fleet with real cluster ID shards RLN on * fix: stop fleet tests when RLN registration fails * fix: refactor monkeypatch * fix: light_push tests * fix: scoped assertion for store test in fleet mode * fix: reduce comments * fix: different propagation delay for fleet test * fix: add fresh timestamp helper * fix: reduce comments * test: change to Waku v0.38.0 image temporarily * fix: reduce log message * fix: undo reduce log message * fix: add scheduled run at 2 am. * fix: fail fleet tests instead of skip when RLN is not working * fix: refactor get_sample_timestamps * fix: remove on push trigger for fleet wf - reset back to use the latest docker image
298 lines
12 KiB
Python
298 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
||
import inspect
|
||
import glob
|
||
import random
|
||
import string
|
||
from src.libs.custom_logger import get_custom_logger
|
||
import os
|
||
import pytest
|
||
from datetime import datetime
|
||
from time import time
|
||
from uuid import uuid4
|
||
from src.libs.common import attach_allure_file, gen_step_id
|
||
import src.env_vars as env_vars
|
||
from src.env_vars import FLEET_PRIMARY_MULTIADDR, FLEET_DNS_DISCOVERY_URL, FLEET_N1_MULTIADDR, FLEET_N2_MULTIADDR
|
||
from src.data_storage import DS
|
||
from src.postgres_setup import start_postgres, stop_postgres
|
||
from src.test_data import FLEET_CLUSTER_ID, FLEET_PUBSUB_TOPICS, PUBSUB_TOPICS_RLN, VALID_PUBSUB_TOPICS
|
||
from src.test_config import PubsubConfig
|
||
|
||
logger = get_custom_logger(__name__)
|
||
|
||
|
||
def pytest_addoption(parser):
|
||
"""Register the --fleet command-line option."""
|
||
parser.addoption(
|
||
"--fleet",
|
||
action="store_true",
|
||
default=False,
|
||
help=(
|
||
"Bootstrap every local nwaku Docker node against the live waku.test "
|
||
"fleet (node-01.do-ams3 / gc-us-central1-a / ac-cn-hongkong-c). "
|
||
"Also activatable via FLEET_BOOTSTRAP=true env var."
|
||
),
|
||
)
|
||
|
||
|
||
def _fleet_bootstrap_enabled(config) -> bool:
|
||
"""Return True when fleet bootstrap should be activated."""
|
||
|
||
if config.getoption("--fleet", default=False):
|
||
return True
|
||
return os.getenv("FLEET_BOOTSTRAP", "false").lower() == "true"
|
||
|
||
|
||
@pytest.fixture(scope="session")
|
||
def fleet_rln_state(request):
|
||
"""Register 2 RLN memberships once per test session when ``--fleet`` is active."""
|
||
if not _fleet_bootstrap_enabled(request.config):
|
||
yield {"keystore_prefixes": [], "rln_membership_indexes": []}
|
||
return
|
||
|
||
from src.node.waku_node import WakuNode
|
||
from src.env_vars import RLN_CREDENTIALS, DEFAULT_NWAKU
|
||
|
||
if not RLN_CREDENTIALS:
|
||
logger.info("Fleet RLN: RLN_CREDENTIALS not set – nodes will start without RLN")
|
||
yield {"keystore_prefixes": [], "rln_membership_indexes": []}
|
||
return
|
||
|
||
state: dict = {"keystore_prefixes": [], "rln_membership_indexes": []}
|
||
try:
|
||
for i in range(2):
|
||
prefix = "".join(random.choices(string.ascii_lowercase, k=4))
|
||
node = WakuNode(DEFAULT_NWAKU, f"rln_reg_{i + 1}_{gen_step_id()}")
|
||
membership_index = node.register_rln(
|
||
rln_keystore_prefix=prefix,
|
||
rln_creds_source=RLN_CREDENTIALS,
|
||
rln_creds_id=str(i + 1),
|
||
)
|
||
state["keystore_prefixes"].append(prefix)
|
||
state["rln_membership_indexes"].append(membership_index)
|
||
logger.info(
|
||
"Fleet RLN: registered %d memberships – indexes=%s prefixes=%s",
|
||
len(state["rln_membership_indexes"]),
|
||
state["rln_membership_indexes"],
|
||
state["keystore_prefixes"],
|
||
)
|
||
except BaseException as ex:
|
||
logger.error("Fleet RLN: registration failed – aborting test session: %s", ex)
|
||
pytest.exit(f"Fleet RLN registration failed – aborting session: {ex}", returncode=1)
|
||
|
||
yield state
|
||
|
||
|
||
@pytest.fixture(scope="session")
|
||
def pubsub_cfg(request) -> PubsubConfig:
|
||
"""Return the pubsub-topic configuration for the current session."""
|
||
|
||
if _fleet_bootstrap_enabled(request.config):
|
||
return PubsubConfig(
|
||
relay_test_topic=FLEET_PUBSUB_TOPICS[1],
|
||
filter_test_topic=FLEET_PUBSUB_TOPICS[1],
|
||
filter_second_topic=FLEET_PUBSUB_TOPICS[2],
|
||
lightpush_test_topic=FLEET_PUBSUB_TOPICS[0],
|
||
store_test_topic=FLEET_PUBSUB_TOPICS[0],
|
||
rln_test_topic=FLEET_PUBSUB_TOPICS[0],
|
||
all_topics=FLEET_PUBSUB_TOPICS,
|
||
)
|
||
return PubsubConfig(
|
||
relay_test_topic=VALID_PUBSUB_TOPICS[1],
|
||
filter_test_topic=VALID_PUBSUB_TOPICS[1],
|
||
filter_second_topic=VALID_PUBSUB_TOPICS[2],
|
||
lightpush_test_topic=VALID_PUBSUB_TOPICS[0],
|
||
store_test_topic=VALID_PUBSUB_TOPICS[0],
|
||
rln_test_topic=PUBSUB_TOPICS_RLN[0],
|
||
all_topics=VALID_PUBSUB_TOPICS,
|
||
)
|
||
|
||
|
||
@pytest.fixture(scope="session", autouse=True)
|
||
def configure_fleet_bootstrap(request, fleet_rln_state):
|
||
"""Register ``FleetBootstrapConfig`` as ``WakuNode._pre_start_hook`` for the session."""
|
||
|
||
if not _fleet_bootstrap_enabled(request.config):
|
||
logger.info("Fleet bootstrap inactive – pass --fleet (or set FLEET_BOOTSTRAP=true) " "to connect local nodes to the waku.test fleet")
|
||
yield
|
||
return
|
||
|
||
os.environ["FLEET_BOOTSTRAP"] = "true"
|
||
|
||
from src.node.fleet_waku_node import FleetBootstrapConfig
|
||
from src.node.waku_node import WakuNode
|
||
|
||
cfg = FleetBootstrapConfig(fleet_rln_state=fleet_rln_state)
|
||
WakuNode._pre_start_hook = cfg.prepare_start_kwargs
|
||
logger.info(
|
||
"Fleet bootstrap active – NODE1→%s NODE2→%s (additional nodes→%s) dns_discovery_url=%s",
|
||
FLEET_N1_MULTIADDR,
|
||
FLEET_N2_MULTIADDR,
|
||
FLEET_PRIMARY_MULTIADDR,
|
||
FLEET_DNS_DISCOVERY_URL,
|
||
)
|
||
yield
|
||
WakuNode._pre_start_hook = None
|
||
|
||
|
||
@pytest.fixture(scope="function", autouse=True)
|
||
def skip_fleet_test_without_rln(request, fleet_rln_state):
|
||
"""Skip tests marked @pytest.mark.waku_test_fleet when no RLN keystore is
|
||
available for the current session.
|
||
"""
|
||
if not _fleet_bootstrap_enabled(request.config):
|
||
return
|
||
if not request.node.get_closest_marker("waku_test_fleet"):
|
||
return
|
||
if not fleet_rln_state.get("keystore_prefixes"):
|
||
pytest.fail("Failing fleet tests: RLN keystore not available " "(RLN_CREDENTIALS not set or on-chain registration failed)")
|
||
|
||
|
||
@pytest.fixture(scope="session", autouse=True)
|
||
def configure_fleet_cluster(request, pubsub_cfg):
|
||
"""Apply fleet cluster configuration to step classes when ``--fleet`` is active."""
|
||
|
||
if not _fleet_bootstrap_enabled(request.config):
|
||
yield
|
||
return
|
||
|
||
from src.steps.relay import StepsRelay
|
||
from src.steps.filter import StepsFilter
|
||
from src.steps.light_push import StepsLightPush
|
||
from src.steps.store import StepsStore
|
||
from src.steps.rln import StepsRLN
|
||
import tests.relay.test_publish as _relay_publish_mod
|
||
|
||
# Override step-class topic attributes with fleet cluster-1 topics.
|
||
StepsRelay.test_pubsub_topic = pubsub_cfg.relay_test_topic
|
||
StepsFilter.test_pubsub_topic = pubsub_cfg.filter_test_topic
|
||
StepsFilter.second_pubsub_topic = pubsub_cfg.filter_second_topic
|
||
StepsLightPush.test_pubsub_topic = pubsub_cfg.lightpush_test_topic
|
||
StepsStore.test_pubsub_topic = pubsub_cfg.store_test_topic
|
||
StepsRLN.test_pubsub_topic = pubsub_cfg.rln_test_topic
|
||
|
||
# tests/relay/test_publish.py::test_publish_on_multiple_pubsub_topics iterates
|
||
# over the module-level VALID_PUBSUB_TOPICS import directly; rebind it.
|
||
_relay_publish_mod.VALID_PUBSUB_TOPICS = pubsub_cfg.all_topics
|
||
|
||
def _fleet_setup_lightpush_node(self, image, node_index, **kwargs):
|
||
from src.node.waku_node import WakuNode
|
||
|
||
node = WakuNode(image, f"lightpush_node{node_index}_{self.test_id}")
|
||
fleet_kwargs = dict(kwargs)
|
||
fleet_kwargs["relay"] = "false"
|
||
fleet_kwargs["lightpush"] = "false"
|
||
fleet_kwargs["skip_fleet_peering"] = True
|
||
fleet_kwargs.setdefault("cluster_id", FLEET_CLUSTER_ID)
|
||
fleet_kwargs.setdefault("shard", list(range(8)))
|
||
|
||
lightpush_service_addr = self.multiaddr_list[0]
|
||
node.start(lightpushnode=lightpush_service_addr, **fleet_kwargs)
|
||
self.add_node_peer(node, self.multiaddr_list)
|
||
logger.debug(
|
||
"fleet _fleet_setup_lightpush_node: node %d started with relay=false, " "skip_fleet_peering=True, lightpushnode=%s",
|
||
node_index,
|
||
lightpush_service_addr,
|
||
)
|
||
return node
|
||
|
||
StepsLightPush.setup_lightpush_node = _fleet_setup_lightpush_node
|
||
StepsLightPush.default_message_propagation_delay = 0.5
|
||
|
||
logger.info(
|
||
"Fleet cluster config active – pubsub topics overridden to cluster-id=%s "
|
||
"(shards 0-7, e.g. relay_test_topic=%s rln_test_topic=%s); "
|
||
"StepsLightPush.setup_lightpush_node overridden to use receiving_node1 as "
|
||
"lightpush service (fleet-peered with RLN membership #1; messages relay "
|
||
"through fleet mesh to receiving_node2 peered with %s)",
|
||
FLEET_CLUSTER_ID,
|
||
pubsub_cfg.relay_test_topic,
|
||
pubsub_cfg.rln_test_topic,
|
||
FLEET_N2_MULTIADDR,
|
||
)
|
||
yield
|
||
|
||
|
||
# See https://docs.pytest.org/en/latest/example/simple.html#making-test-result-information-available-in-fixtures
|
||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||
def pytest_runtest_makereport(item):
|
||
outcome = yield
|
||
rep = outcome.get_result()
|
||
if rep.when == "call":
|
||
setattr(item, "rep_call", rep)
|
||
return rep
|
||
return None
|
||
|
||
|
||
@pytest.fixture(scope="session", autouse=True)
|
||
def set_allure_env_variables():
|
||
yield
|
||
if os.path.isdir("allure-results") and not os.path.isfile(os.path.join("allure-results", "environment.properties")):
|
||
logger.debug(f"Running fixture teardown: {inspect.currentframe().f_code.co_name}")
|
||
with open(os.path.join("allure-results", "environment.properties"), "w") as outfile:
|
||
for attribute_name in dir(env_vars):
|
||
if attribute_name.isupper():
|
||
attribute_value = getattr(env_vars, attribute_name)
|
||
outfile.write(f"{attribute_name}={attribute_value}\n")
|
||
|
||
|
||
@pytest.fixture(scope="function", autouse=False)
|
||
def start_postgres_container():
|
||
pg_container = start_postgres()
|
||
yield
|
||
stop_postgres(pg_container)
|
||
|
||
|
||
@pytest.fixture(scope="function", autouse=True)
|
||
def test_id(request):
|
||
# setting up an unique test id to be used where needed
|
||
logger.debug(f"Running fixture setup: {inspect.currentframe().f_code.co_name}")
|
||
request.cls.test_id = f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}__{str(uuid4())}"
|
||
|
||
|
||
@pytest.fixture(scope="function", autouse=True)
|
||
def test_setup(request, test_id):
|
||
logger.debug(f"Running test: {request.node.name} with id: {request.cls.test_id}")
|
||
yield
|
||
logger.debug(f"Running fixture teardown: {inspect.currentframe().f_code.co_name}")
|
||
for file in glob.glob(os.path.join(env_vars.DOCKER_LOG_DIR, "*")):
|
||
if os.path.getmtime(file) < time() - 3600:
|
||
logger.debug(f"Deleting old log file: {file}")
|
||
try:
|
||
os.remove(file)
|
||
except:
|
||
logger.error("Could not delete file")
|
||
|
||
|
||
@pytest.fixture(scope="function", autouse=True)
|
||
def attach_logs_on_fail(request):
|
||
yield
|
||
if env_vars.RUNNING_IN_CI and hasattr(request.node, "rep_call") and request.node.rep_call.failed:
|
||
logger.debug(f"Running fixture teardown: {inspect.currentframe().f_code.co_name}")
|
||
logger.debug("Test failed, attempting to attach logs to the allure reports")
|
||
for file in glob.glob(os.path.join(env_vars.DOCKER_LOG_DIR, "*" + request.cls.test_id + "*")):
|
||
attach_allure_file(file)
|
||
|
||
|
||
@pytest.fixture(scope="function", autouse=True)
|
||
def close_open_nodes(attach_logs_on_fail):
|
||
DS.waku_nodes = []
|
||
yield
|
||
logger.debug(f"Running fixture teardown: {inspect.currentframe().f_code.co_name}")
|
||
crashed_containers = []
|
||
for node in DS.waku_nodes:
|
||
try:
|
||
node.stop()
|
||
except Exception as ex:
|
||
if "No such container" in str(ex):
|
||
crashed_containers.append(node.image)
|
||
logger.error(f"Failed to stop container because of error {ex}")
|
||
assert not crashed_containers, f"Containers {crashed_containers} crashed during the test!!!"
|
||
|
||
|
||
@pytest.fixture(scope="function", autouse=True)
|
||
def check_waku_log_errors():
|
||
yield
|
||
logger.debug(f"Running fixture teardown: {inspect.currentframe().f_code.co_name}")
|
||
for node in DS.waku_nodes:
|
||
node.check_waku_log_errors()
|