test: fix type annotations and code quality issues in rate limiting tests

This commit is contained in:
Louis King
2025-09-25 20:05:25 +01:00
parent bc121f2e1b
commit 95bfed2c5c
+101 -63
View File
@@ -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
),
)
)