mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-03-30 12:05:38 +00:00
- Updated `meshcore` dependency version to `2.2.14` in both `pyproject.toml` and `requirements.txt`. - Added multi-byte path support in the `PathCommand`, allowing for 1-, 2-, and 3-byte-per-hop paths. - Enhanced `MessageHandler` to utilize `routing_info` for accurate path extraction and validation. - Improved path extraction methods in `MultitestCommand` and `TestCommand` to prefer `routing_info` for node IDs. - Refactored path handling logic across various commands to ensure consistent multi-byte path processing.
147 lines
5.7 KiB
Python
147 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Unit tests for PathCommand multi-byte path support: routing_info usage and comma/prefix parsing.
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import Mock
|
|
from modules.commands.path_command import PathCommand
|
|
from modules.models import MeshMessage
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestPathCommandDecodePathMultibyte:
|
|
"""Test _decode_path parsing: comma-separated inference and parse_path_string fallback."""
|
|
|
|
@pytest.fixture
|
|
def path_command(self, mock_bot):
|
|
"""Create a PathCommand instance."""
|
|
return PathCommand(mock_bot)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_comma_separated_two_byte_infers_four_char_nodes(self, path_command, mock_bot):
|
|
"""path 0102,5f7e -> 2 nodes (0102, 5F7E) even when prefix_hex_chars=2 would give 4 nodes for continuous."""
|
|
mock_bot.prefix_hex_chars = 2
|
|
captured = []
|
|
|
|
async def capture_lookup(node_ids, lookup_func=None):
|
|
captured.append(node_ids)
|
|
return {nid: {'found': True, 'name': nid} for nid in node_ids}
|
|
|
|
path_command._lookup_repeater_names = capture_lookup
|
|
await path_command._decode_path("0102,5f7e")
|
|
assert len(captured) == 1
|
|
assert captured[0] == ['0102', '5F7E']
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_comma_separated_one_byte_two_nodes(self, path_command, mock_bot):
|
|
"""path 01,5f -> 2 nodes (01, 5F)."""
|
|
mock_bot.prefix_hex_chars = 2
|
|
captured = []
|
|
|
|
async def capture_lookup(node_ids, lookup_func=None):
|
|
captured.append(node_ids)
|
|
return {nid: {'found': True, 'name': nid} for nid in node_ids}
|
|
|
|
path_command._lookup_repeater_names = capture_lookup
|
|
await path_command._decode_path("01,5f")
|
|
assert len(captured) == 1
|
|
assert captured[0] == ['01', '5F']
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_continuous_hex_uses_prefix_hex_chars(self, path_command, mock_bot):
|
|
"""path 01025f7e: prefix_hex_chars=2 -> 4 nodes (1-byte); prefix_hex_chars=4 -> 2 nodes (2-byte)."""
|
|
captured = []
|
|
|
|
async def capture_lookup(node_ids, lookup_func=None):
|
|
captured.append(list(node_ids))
|
|
return {nid: {'found': True, 'name': nid} for nid in node_ids}
|
|
|
|
path_command._lookup_repeater_names = capture_lookup
|
|
|
|
mock_bot.prefix_hex_chars = 2 # 2 hex chars per node = 1 byte per hop
|
|
captured.clear()
|
|
await path_command._decode_path("01025f7e")
|
|
assert len(captured) == 1
|
|
assert captured[0] == ['01', '02', '5F', '7E']
|
|
|
|
mock_bot.prefix_hex_chars = 4 # 4 hex chars per node = 2 bytes per hop
|
|
captured.clear()
|
|
await path_command._decode_path("01025f7e")
|
|
assert len(captured) == 1
|
|
assert captured[0] == ['0102', '5F7E']
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_strips_hop_count_suffix(self, path_command, mock_bot):
|
|
"""path 01,5f (2 hops) -> 2 nodes."""
|
|
mock_bot.prefix_hex_chars = 2
|
|
captured = []
|
|
|
|
async def capture_lookup(node_ids, lookup_func=None):
|
|
captured.append(node_ids)
|
|
return {nid: {'found': True, 'name': nid} for nid in node_ids}
|
|
|
|
path_command._lookup_repeater_names = capture_lookup
|
|
await path_command._decode_path("01,5f (2 hops)")
|
|
assert len(captured) == 1
|
|
assert captured[0] == ['01', '5F']
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestPathCommandExtractPathUsesRoutingInfo:
|
|
"""Test _extract_path_from_recent_messages prefers routing_info.path_nodes when present."""
|
|
|
|
@pytest.fixture
|
|
def path_command(self, mock_bot):
|
|
"""Create a PathCommand instance."""
|
|
return PathCommand(mock_bot)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_uses_routing_info_path_nodes_when_present(self, path_command, mock_bot):
|
|
"""When message has routing_info with path_nodes, use them directly (no _decode_path)."""
|
|
path_command._current_message = MeshMessage(
|
|
content="path",
|
|
path="0102,5f7e (2 hops via FLOOD)",
|
|
routing_info={
|
|
'path_length': 2,
|
|
'path_nodes': ['0102', '5f7e'],
|
|
'path_hex': '01025f7e',
|
|
'bytes_per_hop': 2,
|
|
'route_type': 'FLOOD',
|
|
},
|
|
)
|
|
captured = []
|
|
|
|
async def capture_lookup(node_ids, lookup_func=None):
|
|
captured.append(list(node_ids))
|
|
return {nid: {'found': True, 'name': nid} for nid in node_ids}
|
|
|
|
path_command._lookup_repeater_names = capture_lookup
|
|
decode_path_called = []
|
|
|
|
async def track_decode(path_input):
|
|
decode_path_called.append(path_input)
|
|
return "decode_path_result"
|
|
|
|
path_command._decode_path = track_decode
|
|
path_command.translate = lambda key, **kwargs: f"msg:{kwargs.get('node_id', kwargs.get('name', key))}"
|
|
|
|
result = await path_command._extract_path_from_recent_messages()
|
|
|
|
assert len(captured) == 1
|
|
assert captured[0] == ['0102', '5F7E']
|
|
assert len(decode_path_called) == 0, "Should not call _decode_path when routing_info.path_nodes present"
|
|
assert '0102' in result and '5F7E' in result
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_direct_connection_when_routing_info_path_length_zero(self, path_command, mock_bot):
|
|
"""When routing_info.path_length is 0, return direct connection message."""
|
|
path_command._current_message = MeshMessage(
|
|
content="path",
|
|
path="Direct via FLOOD",
|
|
routing_info={'path_length': 0, 'path_nodes': [], 'route_type': 'FLOOD'},
|
|
)
|
|
path_command.translate = lambda key, **kwargs: "Direct connection" if "direct" in key.lower() else key
|
|
result = await path_command._extract_path_from_recent_messages()
|
|
assert "direct" in result.lower()
|