Files
meshcore-mqtt/tests/test_command_forwarding.py
claude[bot] e94f065451 fix: update default TCP port from 12345 to 5000 in code and tests
Updates the default TCP port throughout the codebase to match the
documentation changes made in the previous commit.

Changes:
- config.py: Update TCP port validator default (line 136)
- config.py: Update environment variable fallback default (line 255)
- test_config.py: Update test assertions (lines 117, 145)
- test_bridge.py: Update test assertion and fixture (lines 23, 63)
- test_command_forwarding.py: Update test fixture (line 20)
- test_rate_limiting.py: Update test fixtures (lines 22, 37)

Co-authored-by: JingleManSweep <jinglemansweep@users.noreply.github.com>
2025-11-16 22:32:55 +00:00

223 lines
8.3 KiB
Python

"""Tests for MQTT command forwarding to MeshCore."""
from typing import Any
from unittest.mock import AsyncMock, MagicMock
import pytest
from meshcore_mqtt.config import Config, ConnectionType, MeshCoreConfig, MQTTConfig
from meshcore_mqtt.meshcore_client import MeshCoreClientManager
@pytest.fixture
def test_config() -> Config:
"""Create a test configuration."""
return Config(
mqtt=MQTTConfig(broker="localhost"),
meshcore=MeshCoreConfig(
connection_type=ConnectionType.TCP,
address="127.0.0.1",
port=5000,
),
)
@pytest.fixture
def meshcore_manager(test_config: Config) -> MeshCoreClientManager:
"""Create a MeshCore manager instance for testing."""
return MeshCoreClientManager(test_config)
class TestCommandForwarding:
"""Test MQTT command forwarding to MeshCore."""
async def test_send_msg_command(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test send_msg command forwarding."""
# Setup mock MeshCore instance
mock_meshcore = MagicMock()
mock_meshcore.commands = MagicMock()
mock_meshcore.commands.send_msg = AsyncMock()
meshcore_manager.meshcore = mock_meshcore
# Test send_msg command
command_data = {"destination": "Alice", "message": "Hello!"}
await meshcore_manager.send_command("send_msg", command_data)
# Verify the command was called
mock_meshcore.commands.send_msg.assert_called_once_with("Alice", "Hello!")
async def test_device_query_command(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test device_query command forwarding."""
# Setup mock MeshCore instance
mock_meshcore = MagicMock()
mock_meshcore.commands = MagicMock()
mock_meshcore.commands.send_device_query = AsyncMock()
meshcore_manager.meshcore = mock_meshcore
# Test device_query command
await meshcore_manager.send_command("device_query", {})
# Verify the command was called
mock_meshcore.commands.send_device_query.assert_called_once()
async def test_ping_command(self, meshcore_manager: MeshCoreClientManager) -> None:
"""Test ping command forwarding."""
# Setup mock MeshCore instance
mock_meshcore = MagicMock()
mock_meshcore.commands = MagicMock()
mock_meshcore.commands.ping = AsyncMock()
meshcore_manager.meshcore = mock_meshcore
# Test ping command
command_data = {"destination": "node123"}
await meshcore_manager.send_command("ping", command_data)
# Verify the command was called
mock_meshcore.commands.ping.assert_called_once_with("node123")
async def test_set_name_command(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test set_name command forwarding."""
# Setup mock MeshCore instance
mock_meshcore = MagicMock()
mock_meshcore.commands = MagicMock()
mock_meshcore.commands.set_name = AsyncMock()
meshcore_manager.meshcore = mock_meshcore
# Test set_name command
command_data = {"name": "MyDevice"}
await meshcore_manager.send_command("set_name", command_data)
# Verify the command was called
mock_meshcore.commands.set_name.assert_called_once_with("MyDevice")
async def test_send_chan_msg_command(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test send_chan_msg command forwarding."""
# Setup mock MeshCore instance
mock_meshcore = MagicMock()
mock_meshcore.commands = MagicMock()
mock_meshcore.commands.send_chan_msg = AsyncMock()
meshcore_manager.meshcore = mock_meshcore
# Test send_chan_msg command
command_data = {"channel": 0, "message": "Hello channel!"}
await meshcore_manager.send_command("send_chan_msg", command_data)
# Verify the command was called
mock_meshcore.commands.send_chan_msg.assert_called_once_with(
0, "Hello channel!"
)
async def test_missing_required_fields(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test command validation for missing required fields."""
# Setup mock MeshCore instance
mock_meshcore = MagicMock()
mock_meshcore.commands = MagicMock()
mock_meshcore.commands.send_msg = AsyncMock()
meshcore_manager.meshcore = mock_meshcore
# Test send_msg without required fields
command_data = {"message": "Hello!"} # Missing destination
await meshcore_manager.send_command("send_msg", command_data)
# Verify the command was NOT called due to validation
mock_meshcore.commands.send_msg.assert_not_called()
# Setup mock for send_chan_msg
mock_meshcore.commands.send_chan_msg = AsyncMock()
# Test send_chan_msg without required fields
command_data = {"message": "Hello channel!"} # Missing channel
await meshcore_manager.send_command("send_chan_msg", command_data)
# Verify the command was NOT called due to validation
mock_meshcore.commands.send_chan_msg.assert_not_called()
# Test send_chan_msg with None channel
command_data_none: dict[str, Any] = {
"channel": None,
"message": "Hello channel!",
}
await meshcore_manager.send_command("send_chan_msg", command_data_none)
# Verify the command was NOT called due to validation
mock_meshcore.commands.send_chan_msg.assert_not_called()
# Test send_chan_msg without message
command_data_no_msg: dict[str, Any] = {"channel": 0} # Missing message
await meshcore_manager.send_command("send_chan_msg", command_data_no_msg)
# Verify the command was NOT called due to validation
mock_meshcore.commands.send_chan_msg.assert_not_called()
async def test_unknown_command_type(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test handling of unknown command types."""
# Setup mock MeshCore instance
mock_meshcore = MagicMock()
meshcore_manager.meshcore = mock_meshcore
# Test unknown command
await meshcore_manager.send_command("unknown_command", {})
# Should not raise an exception, just log a warning
async def test_no_meshcore_instance(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test command handling when MeshCore instance is None."""
# Ensure meshcore is None
meshcore_manager.meshcore = None
# Test command - should not raise exception
await meshcore_manager.send_command(
"send_msg", {"destination": "test", "message": "test"}
)
async def test_command_error_handling(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test error handling when MeshCore command fails."""
# Setup mock MeshCore instance that raises an exception
mock_meshcore = MagicMock()
mock_meshcore.commands = MagicMock()
mock_meshcore.commands.send_msg = AsyncMock(side_effect=Exception("Test error"))
meshcore_manager.meshcore = mock_meshcore
# Test command - should not raise exception, just log error
command_data = {"destination": "Alice", "message": "Hello!"}
await meshcore_manager.send_command("send_msg", command_data)
# Verify the command was attempted
mock_meshcore.commands.send_msg.assert_called_once_with("Alice", "Hello!")
async def test_activity_update_on_successful_command(
self, meshcore_manager: MeshCoreClientManager
) -> None:
"""Test that activity timestamp is updated on successful commands."""
# Setup mock MeshCore instance
mock_meshcore = MagicMock()
mock_meshcore.commands = MagicMock()
mock_meshcore.commands.send_device_query = AsyncMock(return_value=None)
meshcore_manager.meshcore = mock_meshcore
# Mock the update_activity method using setattr to avoid mypy error
mock_update_activity = MagicMock()
setattr(meshcore_manager, "update_activity", mock_update_activity)
# Test device_query command (no result object)
await meshcore_manager.send_command("device_query", {})
# Verify activity was updated
mock_update_activity.assert_called_once()