Skip to content

Commit 9aa987d

Browse files
authored
Adding more e2e tests related to maintenance notifications. Extracting specific tests that validate notifications are received in new connections (#3770)
1 parent 8403ddc commit 9aa987d

File tree

4 files changed

+402
-46
lines changed

4 files changed

+402
-46
lines changed

tests/test_scenario/conftest.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,14 @@ def client_maint_events(endpoints_config):
5555

5656
def _get_client_maint_events(
5757
endpoints_config,
58+
protocol: int = 3,
5859
enable_maintenance_events: bool = True,
5960
endpoint_type: Optional[EndpointType] = None,
6061
enable_relax_timeout: bool = True,
6162
enable_proactive_reconnect: bool = True,
6263
disable_retries: bool = False,
6364
socket_timeout: Optional[float] = None,
65+
host_config: Optional[str] = None,
6466
):
6567
"""Create Redis client with maintenance events enabled."""
6668

@@ -74,11 +76,9 @@ def _get_client_maint_events(
7476
raise ValueError("No endpoints found in configuration")
7577

7678
parsed = urlparse(endpoints[0])
77-
host = parsed.hostname
79+
host = parsed.hostname if host_config is None else host_config
7880
port = parsed.port
7981

80-
tls_enabled = True if parsed.scheme == "rediss" else False
81-
8282
if not host:
8383
raise ValueError(f"Could not parse host from endpoint URL: {endpoints[0]}")
8484

@@ -99,6 +99,9 @@ def _get_client_maint_events(
9999
else:
100100
retry = Retry(backoff=ExponentialWithJitterBackoff(base=1, cap=10), retries=3)
101101

102+
tls_enabled = True if parsed.scheme == "rediss" else False
103+
logging.info(f"TLS enabled: {tls_enabled}")
104+
102105
client = Redis(
103106
host=host,
104107
port=port,
@@ -108,7 +111,7 @@ def _get_client_maint_events(
108111
ssl=tls_enabled,
109112
ssl_cert_reqs="none",
110113
ssl_check_hostname=False,
111-
protocol=3, # RESP3 required for push notifications
114+
protocol=protocol, # RESP3 required for push notifications
112115
maintenance_events_config=maintenance_config,
113116
retry=retry,
114117
)

tests/test_scenario/fault_injector_client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import time
44
import urllib.request
5+
import urllib.error
56
from typing import Dict, Any, Optional, Union
67
from enum import Enum
78

@@ -94,7 +95,7 @@ def get_action_status(self, action_id: str) -> Dict[str, Any]:
9495
return self._make_request("GET", f"/action/{action_id}")
9596

9697
def execute_rladmin_command(
97-
self, command: str, bdb_id: str = None
98+
self, command: str, bdb_id: Optional[str] = None
9899
) -> Dict[str, Any]:
99100
"""Execute rladmin command directly as string"""
100101
url = f"{self.base_url}/rladmin"
@@ -146,4 +147,4 @@ def get_operation_result(
146147
logging.warning(f"Error checking operation status: {e}")
147148
time.sleep(check_interval)
148149
else:
149-
raise TimeoutError(f"Timeout waiting for operation {action_id}")
150+
pytest.fail(f"Timeout waiting for operation {action_id}")

tests/test_scenario/hitless_upgrade_helpers.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class ClientValidations:
1717
def wait_push_notification(
1818
redis_client: Redis,
1919
timeout: int = 120,
20+
fail_on_timeout: bool = True,
2021
connection: Optional[Connection] = None,
2122
):
2223
"""Wait for a push notification to be received."""
@@ -35,11 +36,15 @@ def wait_push_notification(
3536
logging.debug(
3637
f"Push notification has been received. Response: {push_response}"
3738
)
39+
if test_conn.should_reconnect():
40+
logging.debug("Connection is marked for reconnect")
3841
return
3942
except Exception as e:
4043
logging.error(f"Error reading push notification: {e}")
4144
break
4245
time.sleep(check_interval)
46+
if fail_on_timeout:
47+
pytest.fail("Timeout waiting for push notification")
4348
finally:
4449
# Release the connection back to the pool
4550
try:
@@ -215,6 +220,40 @@ def find_endpoint_for_bind(
215220

216221
raise ValueError(f"No endpoint ID for {endpoint_name} found in cluster status")
217222

223+
@staticmethod
224+
def execute_failover(
225+
fault_injector: FaultInjectorClient,
226+
endpoint_config: Dict[str, Any],
227+
timeout: int = 60,
228+
) -> Dict[str, Any]:
229+
"""Execute failover command and wait for completion."""
230+
231+
try:
232+
bdb_id = endpoint_config.get("bdb_id")
233+
failover_action = ActionRequest(
234+
action_type=ActionType.FAILOVER,
235+
parameters={
236+
"bdb_id": bdb_id,
237+
},
238+
)
239+
trigger_action_result = fault_injector.trigger_action(failover_action)
240+
action_id = trigger_action_result.get("action_id")
241+
if not action_id:
242+
raise ValueError(
243+
f"Failed to trigger fail over action for bdb_id {bdb_id}: {trigger_action_result}"
244+
)
245+
246+
action_status_check_response = fault_injector.get_operation_result(
247+
action_id, timeout=timeout
248+
)
249+
logging.info(
250+
f"Completed cluster nodes info reading: {action_status_check_response}"
251+
)
252+
return action_status_check_response
253+
254+
except Exception as e:
255+
pytest.fail(f"Failed to get cluster nodes info: {e}")
256+
218257
@staticmethod
219258
def execute_rladmin_migrate(
220259
fault_injector: FaultInjectorClient,

0 commit comments

Comments
 (0)