Files
meshcore-bot/tests/unit/test_path_command_graph.py
agessaman d219eb4f57 Implement probabilistic topology engine and related configurations
- Added new settings for the topology engine in `config.ini.example` to support legacy, shadow, and new modes.
- Updated documentation to include details on the probabilistic topology engine and its configuration options.
- Enhanced `MeshCoreBot` to initialize the topology engine and log its status.
- Introduced new database tables for shadow topology inference, ghost nodes, and model metrics in `RepeaterManager`.
- Modified `PathCommand` to utilize the topology engine for repeater selection and comparison telemetry.
- Updated web viewer templates to display topology engine mode and enable topology validation features.
- Added JavaScript functions to handle data mode switching between legacy and model graphs.
2026-03-13 17:55:30 -07:00

311 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Unit tests for PathCommand graph-based selection logic
"""
import pytest
from unittest.mock import Mock, patch
from datetime import datetime
from modules.commands.path_command import PathCommand
from tests.helpers import create_test_repeater, create_test_edge, populate_test_graph
@pytest.mark.unit
class TestPathCommandGraphSelection:
"""Test PathCommand._select_repeater_by_graph method."""
def test_select_repeater_by_graph_no_graph(self, mock_bot):
"""Test when graph_based_validation is False."""
# Disable graph validation
mock_bot.config.set('Path_Command', 'graph_based_validation', 'false')
path_cmd = PathCommand(mock_bot)
repeaters = [create_test_repeater('01', 'Test Repeater')]
result = path_cmd._select_repeater_by_graph(repeaters, '01', ['01'])
assert result == (None, 0.0, None)
def test_select_repeater_by_graph_no_mesh_graph(self, mock_bot):
"""Test when mesh_graph is None."""
mock_bot.mesh_graph = None
path_cmd = PathCommand(mock_bot)
repeaters = [create_test_repeater('01', 'Test Repeater')]
result = path_cmd._select_repeater_by_graph(repeaters, '01', ['01'])
assert result == (None, 0.0, None)
def test_select_repeater_by_graph_no_context(self, mock_bot, mesh_graph):
"""Test when node_id not in path_context."""
mock_bot.mesh_graph = mesh_graph
path_cmd = PathCommand(mock_bot)
repeaters = [create_test_repeater('99', 'Test Repeater')]
result = path_cmd._select_repeater_by_graph(repeaters, '99', ['01', '7e', '86'])
assert result == (None, 0.0, None)
def test_select_repeater_by_graph_direct_edge(self, mock_bot, mesh_graph):
"""Test selection with strong direct edge."""
mock_bot.mesh_graph = mesh_graph
# Create strong edge: 01 -> 7e
mesh_graph.add_edge('01', '7e')
for _ in range(10):
mesh_graph.add_edge('01', '7e') # 11 total observations
path_cmd = PathCommand(mock_bot)
# Create repeaters with matching prefix
repeaters = [
create_test_repeater('7e', 'Test Repeater 7e', public_key='7e' * 32)
]
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e', '86'])
assert result[0] is not None # Should select a repeater
assert result[1] > 0.7 # High confidence
assert result[2] == 'graph' # Direct edge method
def test_select_repeater_by_graph_stored_public_key_bonus(self, mock_bot, mesh_graph):
"""Test stored public key bonus."""
mock_bot.mesh_graph = mesh_graph
# Create edge with stored public key
public_key = '7e' * 32 # 64 hex chars
mesh_graph.add_edge('01', '7e', from_public_key='01' * 32, to_public_key=public_key)
for _ in range(5):
mesh_graph.add_edge('01', '7e')
path_cmd = PathCommand(mock_bot)
# Create repeater with matching public key
repeaters = [
create_test_repeater('7e', 'Test Repeater', public_key=public_key),
create_test_repeater('7e', 'Other Repeater', public_key='aa' * 32) # Different key
]
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e', '86'])
assert result[0] is not None
# Should select the repeater with matching public key
assert result[0]['public_key'] == public_key
assert result[1] > 0.5 # Should have good confidence with stored key bonus
def test_select_repeater_by_graph_star_bias(self, mock_bot, mesh_graph):
"""Test star bias multiplier application."""
mock_bot.mesh_graph = mesh_graph
# Create edges for both repeaters
mesh_graph.add_edge('01', '7e')
mesh_graph.add_edge('01', '7a')
path_cmd = PathCommand(mock_bot)
# Create one starred and one non-starred repeater
repeaters = [
create_test_repeater('7e', 'Starred Repeater', is_starred=True),
create_test_repeater('7a', 'Regular Repeater', is_starred=False)
]
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e'])
assert result[0] is not None
# Starred repeater should be selected even if both have similar graph scores
assert result[0]['is_starred'] is True
def test_select_repeater_by_graph_multi_hop(self, mock_bot, mesh_graph):
"""Test multi-hop inference when direct edge has low confidence."""
mock_bot.mesh_graph = mesh_graph
# Create 2-hop path: 01 -> 7e -> 86 (no direct 01 -> 86)
# Need at least 3 observations per edge for min_edge_observations
for _ in range(3):
mesh_graph.add_edge('01', '7e')
mesh_graph.add_edge('7e', '86')
path_cmd = PathCommand(mock_bot)
# Create repeater that's the intermediate node
repeaters = [
create_test_repeater('7e', 'Intermediate Repeater', public_key='7e' * 32)
]
# Try to select 7e when path is 01 -> 7e -> 86
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e', '86'])
assert result[0] is not None
# Should use graph method (direct edge exists)
assert result[2] in ('graph', 'graph_multihop')
def test_select_repeater_by_graph_hop_position(self, mock_bot, mesh_graph):
"""Test hop position validation."""
mock_bot.mesh_graph = mesh_graph
# Create edge with avg_hop_position = 1.0
# Need at least 3 observations for min_edge_observations
for _ in range(3):
mesh_graph.add_edge('01', '7e', hop_position=1)
path_cmd = PathCommand(mock_bot)
path_cmd.graph_use_hop_position = True
repeaters = [create_test_repeater('7e', 'Test Repeater')]
# Path where 7e is at position 1
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e', '86'])
assert result[0] is not None
assert result[1] > 0.0
def test_select_repeater_by_graph_multiple_candidates(self, mock_bot, mesh_graph):
"""Test selection from multiple candidates."""
mock_bot.mesh_graph = mesh_graph
# Create edges with different strengths
mesh_graph.add_edge('01', '7e')
for _ in range(10):
mesh_graph.add_edge('01', '7e') # Strong edge
mesh_graph.add_edge('01', '7a')
for _ in range(2):
mesh_graph.add_edge('01', '7a') # Weaker edge
path_cmd = PathCommand(mock_bot)
repeaters = [
create_test_repeater('7e', 'Strong Edge Repeater'),
create_test_repeater('7a', 'Weak Edge Repeater')
]
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e'])
assert result[0] is not None
# Should select the one with stronger edge (7e)
assert result[0]['name'] == 'Strong Edge Repeater'
assert result[1] > 0.5
def test_select_repeater_by_graph_confidence_conversion(self, mock_bot, mesh_graph):
"""Test graph score to confidence conversion."""
mock_bot.mesh_graph = mesh_graph
# Create very strong edge
mesh_graph.add_edge('01', '7e')
for _ in range(20):
mesh_graph.add_edge('01', '7e')
path_cmd = PathCommand(mock_bot)
repeaters = [create_test_repeater('7e', 'Test Repeater')]
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e'])
assert result[0] is not None
# Confidence should be capped at 1.0
assert 0.0 <= result[1] <= 1.0
def test_select_repeater_by_graph_star_bias_exceeds_one(self, mock_bot, mesh_graph):
"""Test star bias can exceed 1.0 but confidence is normalized."""
mock_bot.mesh_graph = mesh_graph
mesh_graph.add_edge('01', '7e')
for _ in range(5):
mesh_graph.add_edge('01', '7e')
path_cmd = PathCommand(mock_bot)
path_cmd.star_bias_multiplier = 2.5 # High multiplier
# Create starred repeater
repeaters = [create_test_repeater('7e', 'Starred Repeater', is_starred=True)]
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e'])
assert result[0] is not None
# Confidence should still be capped appropriately
assert 0.0 <= result[1] <= 1.0
def test_select_repeater_by_graph_prefix_extraction(self, mock_bot, mesh_graph):
"""Test prefix extraction from public_key."""
mock_bot.mesh_graph = mesh_graph
# Create edge
mesh_graph.add_edge('01', '7e')
path_cmd = PathCommand(mock_bot)
# Repeater with public key starting with '7e'
public_key = '7e' + '00' * 31 # 7e prefix
repeaters = [create_test_repeater('7e', 'Test Repeater', public_key=public_key)]
result = path_cmd._select_repeater_by_graph(repeaters, '7e', ['01', '7e'])
assert result[0] is not None
def test_select_repeater_by_graph_missing_public_key(self, mock_bot, mesh_graph):
"""Test handling when public_key is missing."""
mock_bot.mesh_graph = mesh_graph
mesh_graph.add_edge('01', '7e')
path_cmd = PathCommand(mock_bot)
# Repeater without public_key
repeater = create_test_repeater('7e', 'Test Repeater')
del repeater['public_key'] # Remove public key
result = path_cmd._select_repeater_by_graph([repeater], '7e', ['01', '7e'])
# Should skip this repeater (no prefix to match)
assert result == (None, 0.0, None) or result[0] is None
def test_select_repeater_by_graph_path_validation_bonus_applies_without_remainder(self, mock_bot, mesh_graph):
"""Path-validation bonus should apply for well-formed decoded paths (no length remainder)."""
mock_bot.mesh_graph = mesh_graph
path_cmd = PathCommand(mock_bot)
path_cmd.graph_multi_hop_enabled = False
mock_bot.db_manager.create_table(
"observed_paths",
"""
id INTEGER PRIMARY KEY AUTOINCREMENT,
public_key TEXT,
packet_hash TEXT,
from_prefix TEXT NOT NULL,
to_prefix TEXT NOT NULL,
path_hex TEXT NOT NULL,
path_length INTEGER NOT NULL,
bytes_per_hop INTEGER,
packet_type TEXT NOT NULL,
first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
observation_count INTEGER DEFAULT 1
""",
)
candidate_key = "7e" * 32
mock_bot.db_manager.execute_update(
"""
INSERT INTO observed_paths
(public_key, packet_hash, from_prefix, to_prefix, path_hex, path_length, bytes_per_hop, packet_type, first_seen, last_seen, observation_count)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
candidate_key,
"pkt-path-bonus",
"01",
"86",
"017e86",
3,
1,
"advert",
datetime.now().isoformat(),
datetime.now().isoformat(),
25,
),
)
repeaters = [create_test_repeater("7e", "PathBonusCandidate", public_key=candidate_key)]
selected, confidence, method = path_cmd._select_repeater_by_graph(repeaters, "7e", ["01", "7e", "86"])
assert selected is not None
assert selected["public_key"] == candidate_key
assert confidence > 0.0
assert method == "graph"