From 95bfed2c5cd113f0a9f1ff68aa9d9ad4ca049d95 Mon Sep 17 00:00:00 2001 From: Louis King Date: Thu, 25 Sep 2025 20:05:25 +0100 Subject: [PATCH] test: fix type annotations and code quality issues in rate limiting tests --- tests/test_rate_limiting.py | 164 ++++++++++++++++++++++-------------- 1 file changed, 101 insertions(+), 63 deletions(-) diff --git a/tests/test_rate_limiting.py b/tests/test_rate_limiting.py index d5fa6f0..c686083 100644 --- a/tests/test_rate_limiting.py +++ b/tests/test_rate_limiting.py @@ -2,8 +2,8 @@ import asyncio import time -from typing import Any -from unittest.mock import AsyncMock, MagicMock, patch +from typing import Any, Dict +from unittest.mock import AsyncMock, MagicMock import pytest @@ -21,7 +21,7 @@ def test_config() -> Config: address="127.0.0.1", port=12345, message_initial_delay=0.1, # 100ms for faster tests - message_send_delay=0.2, # 200ms for faster tests + message_send_delay=0.2, # 200ms for faster tests ), ) @@ -60,7 +60,9 @@ def mock_meshcore_worker(test_config: Config) -> MeshCoreWorker: class TestRateLimiting: """Test message rate limiting functionality.""" - async def test_message_initial_delay(self, mock_meshcore_worker: MeshCoreWorker) -> None: + async def test_message_initial_delay( + self, mock_meshcore_worker: MeshCoreWorker + ) -> None: """Test that initial delay is applied before the first message.""" start_time = time.time() @@ -69,17 +71,15 @@ class TestRateLimiting: # Queue a message command_data = {"destination": "test", "message": "test"} - future = asyncio.Future() - message_data = { - "command_type": "send_msg", - "future": future, - **command_data - } + future: asyncio.Future[Any] = asyncio.Future() + message_data = {"command_type": "send_msg", "future": future, **command_data} await mock_meshcore_worker._message_queue.put(message_data) # Start the rate limiter - rate_limiter_task = asyncio.create_task(mock_meshcore_worker._message_rate_limiter()) + rate_limiter_task = asyncio.create_task( + mock_meshcore_worker._message_rate_limiter() + ) # Wait for the message to be processed try: @@ -97,9 +97,13 @@ class TestRateLimiting: elapsed_time = time.time() - start_time # Should take at least the initial delay time - assert elapsed_time >= mock_meshcore_worker.config.meshcore.message_initial_delay + assert ( + elapsed_time >= mock_meshcore_worker.config.meshcore.message_initial_delay + ) - async def test_message_send_delay(self, mock_meshcore_worker: MeshCoreWorker) -> None: + async def test_message_send_delay( + self, mock_meshcore_worker: MeshCoreWorker + ) -> None: """Test that delay is applied between consecutive messages.""" # Set worker to running state mock_meshcore_worker._running = True @@ -111,17 +115,15 @@ class TestRateLimiting: # Queue a message command_data = {"destination": "test", "message": "test"} - future = asyncio.Future() - message_data = { - "command_type": "send_msg", - "future": future, - **command_data - } + future: asyncio.Future[Any] = asyncio.Future() + message_data = {"command_type": "send_msg", "future": future, **command_data} await mock_meshcore_worker._message_queue.put(message_data) # Start the rate limiter - rate_limiter_task = asyncio.create_task(mock_meshcore_worker._message_rate_limiter()) + rate_limiter_task = asyncio.create_task( + mock_meshcore_worker._message_rate_limiter() + ) # Wait for the message to be processed try: @@ -141,7 +143,9 @@ class TestRateLimiting: # Should take at least the send delay time assert elapsed_time >= mock_meshcore_worker.config.meshcore.message_send_delay - async def test_no_delay_with_zero_config(self, test_config_zero_delays: Config) -> None: + async def test_no_delay_with_zero_config( + self, test_config_zero_delays: Config + ) -> None: """Test that no delays are applied when configured to zero.""" worker = MeshCoreWorker(test_config_zero_delays) @@ -158,12 +162,8 @@ class TestRateLimiting: # Queue a message command_data = {"destination": "test", "message": "test"} - future = asyncio.Future() - message_data = { - "command_type": "send_msg", - "future": future, - **command_data - } + future: asyncio.Future[Any] = asyncio.Future() + message_data = {"command_type": "send_msg", "future": future, **command_data} await worker._message_queue.put(message_data) @@ -188,50 +188,71 @@ class TestRateLimiting: # Should complete quickly with no delays assert elapsed_time < 0.1 - async def test_rate_limited_command_execution(self, mock_meshcore_worker: MeshCoreWorker) -> None: + async def test_rate_limited_command_execution( + self, mock_meshcore_worker: MeshCoreWorker + ) -> None: """Test that rate-limited commands are executed correctly.""" # Test send_msg command - result = await mock_meshcore_worker._execute_rate_limited_message({ - "command_type": "send_msg", - "destination": "test_user", - "message": "Hello", - "future": None - }) + await mock_meshcore_worker._execute_rate_limited_message( + { + "command_type": "send_msg", + "destination": "test_user", + "message": "Hello", + "future": None, + } + ) # Verify the command was called - mock_meshcore_worker.meshcore.commands.send_msg.assert_called_once_with("test_user", "Hello") + assert mock_meshcore_worker.meshcore is not None + mock_meshcore_worker.meshcore.commands.send_msg.assert_called_once_with( + "test_user", "Hello" + ) # Test send_chan_msg command - await mock_meshcore_worker._execute_rate_limited_message({ - "command_type": "send_chan_msg", - "channel": 0, - "message": "Hello channel", - "future": None - }) + await mock_meshcore_worker._execute_rate_limited_message( + { + "command_type": "send_chan_msg", + "channel": 0, + "message": "Hello channel", + "future": None, + } + ) # Verify the command was called - mock_meshcore_worker.meshcore.commands.send_chan_msg.assert_called_once_with(0, "Hello channel") + assert mock_meshcore_worker.meshcore is not None + mock_meshcore_worker.meshcore.commands.send_chan_msg.assert_called_once_with( + 0, "Hello channel" + ) - async def test_queue_rate_limited_command(self, mock_meshcore_worker: MeshCoreWorker) -> None: + async def test_queue_rate_limited_command( + self, mock_meshcore_worker: MeshCoreWorker + ) -> None: """Test queuing and execution of rate-limited commands.""" # Set worker to running state mock_meshcore_worker._running = True # Start the rate limiter in the background - rate_limiter_task = asyncio.create_task(mock_meshcore_worker._message_rate_limiter()) + rate_limiter_task = asyncio.create_task( + mock_meshcore_worker._message_rate_limiter() + ) try: # Queue a command command_data = {"destination": "test_user", "message": "Hello"} result_task = asyncio.create_task( - mock_meshcore_worker._queue_rate_limited_command("send_msg", command_data) + mock_meshcore_worker._queue_rate_limited_command( + "send_msg", command_data + ) ) # Wait for completion - result = await asyncio.wait_for(result_task, timeout=2.0) + await asyncio.wait_for(result_task, timeout=2.0) # Verify the command was executed - mock_meshcore_worker.meshcore.commands.send_msg.assert_called_once_with("test_user", "Hello") + assert mock_meshcore_worker.meshcore is not None + mock_meshcore_worker.meshcore.commands.send_msg.assert_called_once_with( + "test_user", "Hello" + ) finally: mock_meshcore_worker._running = False # Stop the rate limiter @@ -241,13 +262,17 @@ class TestRateLimiting: except asyncio.CancelledError: pass - async def test_multiple_messages_rate_limiting(self, mock_meshcore_worker: MeshCoreWorker) -> None: + async def test_multiple_messages_rate_limiting( + self, mock_meshcore_worker: MeshCoreWorker + ) -> None: """Test that multiple messages are properly rate limited.""" # Set worker to running state mock_meshcore_worker._running = True # Start the rate limiter - rate_limiter_task = asyncio.create_task(mock_meshcore_worker._message_rate_limiter()) + rate_limiter_task = asyncio.create_task( + mock_meshcore_worker._message_rate_limiter() + ) try: start_time = time.time() @@ -257,7 +282,9 @@ class TestRateLimiting: for i in range(3): command_data = {"destination": f"user{i}", "message": f"Message {i}"} task = asyncio.create_task( - mock_meshcore_worker._queue_rate_limited_command("send_msg", command_data) + mock_meshcore_worker._queue_rate_limited_command( + "send_msg", command_data + ) ) tasks.append(task) @@ -268,12 +295,13 @@ class TestRateLimiting: # Should take at least initial_delay + 2 * send_delay for 3 messages expected_min_time = ( - mock_meshcore_worker.config.meshcore.message_initial_delay + - 2 * mock_meshcore_worker.config.meshcore.message_send_delay + mock_meshcore_worker.config.meshcore.message_initial_delay + + 2 * mock_meshcore_worker.config.meshcore.message_send_delay ) assert elapsed_time >= expected_min_time # Verify all commands were executed + assert mock_meshcore_worker.meshcore is not None assert mock_meshcore_worker.meshcore.commands.send_msg.call_count == 3 finally: @@ -284,24 +312,29 @@ class TestRateLimiting: except asyncio.CancelledError: pass - async def test_rate_limiter_error_handling(self, mock_meshcore_worker: MeshCoreWorker) -> None: + async def test_rate_limiter_error_handling( + self, mock_meshcore_worker: MeshCoreWorker + ) -> None: """Test error handling in rate-limited message execution.""" # Set worker to running state mock_meshcore_worker._running = True - # Make the mock command raise an exception - but we need to bypass the retry logic - # So we'll make it raise an exception in _execute_rate_limited_message directly + # Make the mock command raise an exception - bypass the retry logic + # So we'll make it raise an exception in _execute_rate_limited_message original_execute = mock_meshcore_worker._execute_rate_limited_message - async def mock_execute(message_data): + async def mock_execute(message_data: Dict[str, Any]) -> None: future = message_data.get("future") if future and not future.done(): future.set_exception(Exception("Test error")) - mock_meshcore_worker._execute_rate_limited_message = mock_execute + # Use setattr to avoid mypy error about method assignment + setattr(mock_meshcore_worker, "_execute_rate_limited_message", mock_execute) # Start the rate limiter - rate_limiter_task = asyncio.create_task(mock_meshcore_worker._message_rate_limiter()) + rate_limiter_task = asyncio.create_task( + mock_meshcore_worker._message_rate_limiter() + ) try: # Queue a command that will fail @@ -309,13 +342,18 @@ class TestRateLimiting: with pytest.raises(Exception, match="Test error"): await asyncio.wait_for( - mock_meshcore_worker._queue_rate_limited_command("send_msg", command_data), - timeout=2.0 + mock_meshcore_worker._queue_rate_limited_command( + "send_msg", command_data + ), + timeout=2.0, ) finally: mock_meshcore_worker._running = False # Stop the rate limiter - mock_meshcore_worker._execute_rate_limited_message = original_execute # Restore original + # Restore original method + setattr( + mock_meshcore_worker, "_execute_rate_limited_message", original_execute + ) rate_limiter_task.cancel() try: await rate_limiter_task @@ -344,7 +382,7 @@ class TestRateLimiting: connection_type=ConnectionType.TCP, address="127.0.0.1", message_initial_delay=0.0, # Minimum - message_send_delay=60.0, # Maximum + message_send_delay=60.0, # Maximum ), ) assert config.meshcore.message_initial_delay == 0.0 @@ -369,4 +407,4 @@ class TestRateLimiting: address="127.0.0.1", message_send_delay=61.0, # Invalid: too large ), - ) \ No newline at end of file + )