columba/python/test_path_request.py

154 lines
5.9 KiB
Python
Raw Normal View History

"""
Test suite for path request functionality.
When sending messages to destinations that haven't announced since app start,
Columba should automatically request a path from the network and wait for a response.
"""
import sys
import os
import time
import unittest
from unittest.mock import Mock, MagicMock, patch, call
# Add parent directory to path to import reticulum_wrapper
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Mock RNS and LXMF before importing reticulum_wrapper
mock_rns = MagicMock()
mock_lxmf = MagicMock()
mock_lxmf.LXMessage.OPPORTUNISTIC = 0x01
mock_lxmf.LXMessage.DIRECT = 0x02
mock_lxmf.LXMessage.PROPAGATED = 0x03
mock_lxmf.LXMessage.SENT = 0x04
sys.modules['RNS'] = mock_rns
sys.modules['RNS.vendor'] = MagicMock()
sys.modules['RNS.vendor.platformutils'] = MagicMock()
sys.modules['LXMF'] = mock_lxmf
# Now import after mocking
import reticulum_wrapper
class TestPathRequestIntegration(unittest.TestCase):
"""Integration tests that verify the path request code is present and structured correctly."""
def test_path_request_code_exists_in_send_lxmf_message(self):
"""Verify that send_lxmf_message contains path request logic"""
import inspect
source = inspect.getsource(reticulum_wrapper.ReticulumWrapper.send_lxmf_message)
# Check for key patterns that indicate path request logic
self.assertIn('Transport.request_path', source,
"send_lxmf_message should call Transport.request_path")
self.assertIn('Identity not found, requesting path', source,
"send_lxmf_message should log path request")
self.assertIn('Identity resolved after path request', source,
"send_lxmf_message should log successful resolution")
def test_path_request_code_exists_in_send_lxmf_message_with_method(self):
"""Verify that send_lxmf_message_with_method contains path request logic"""
import inspect
source = inspect.getsource(reticulum_wrapper.ReticulumWrapper.send_lxmf_message_with_method)
# Check for key patterns
self.assertIn('Transport.request_path', source,
"send_lxmf_message_with_method should call Transport.request_path")
self.assertIn('Identity not found, requesting path', source,
"send_lxmf_message_with_method should log path request")
def test_retry_loop_has_correct_timing(self):
"""Verify the retry loop parameters (10 iterations, 0.5s sleep = 5s total)"""
import inspect
source = inspect.getsource(reticulum_wrapper.ReticulumWrapper.send_lxmf_message_with_method)
# Check for the retry loop structure
self.assertIn('range(10)', source,
"Retry loop should iterate 10 times")
self.assertIn('sleep(0.5)', source,
"Should sleep 0.5 seconds between retries")
class TestPathRequestErrorMessages(unittest.TestCase):
"""Tests for correct error messages when path request fails."""
def test_error_message_mentions_path_requested(self):
"""Verify error message indicates path was requested"""
import inspect
source = inspect.getsource(reticulum_wrapper.ReticulumWrapper.send_lxmf_message_with_method)
# The error message should indicate that a path was requested
self.assertIn('Path requested but no response received', source,
"Error message should mention path was requested")
class TestPathRequestMocking(unittest.TestCase):
"""Unit tests using mocking to verify path request behavior."""
def setUp(self):
"""Set up test fixtures"""
import tempfile
self.temp_dir = tempfile.mkdtemp()
def tearDown(self):
"""Clean up test fixtures"""
import shutil
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
def test_wrapper_initialization(self):
"""Test that wrapper initializes correctly with mocked RNS"""
wrapper = reticulum_wrapper.ReticulumWrapper(self.temp_dir)
self.assertIsNotNone(wrapper)
self.assertEqual(wrapper.storage_path, self.temp_dir)
def test_identities_dict_exists(self):
"""Test that wrapper has identities cache"""
wrapper = reticulum_wrapper.ReticulumWrapper(self.temp_dir)
self.assertTrue(hasattr(wrapper, 'identities'))
self.assertIsInstance(wrapper.identities, dict)
def test_path_request_function_callable(self):
"""Test that Transport.request_path is accessible"""
# Verify the mock structure
self.assertTrue(callable(mock_rns.Transport.request_path))
class TestPathRequestLogic(unittest.TestCase):
"""Direct tests of the path request decision logic."""
def setUp(self):
"""Set up test fixtures"""
import tempfile
self.temp_dir = tempfile.mkdtemp()
def tearDown(self):
"""Clean up test fixtures"""
import shutil
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
def test_identity_recall_is_called(self):
"""Test that Identity.recall is called during send preparation"""
# This is a structural test - verify the code path exists
import inspect
source = inspect.getsource(reticulum_wrapper.ReticulumWrapper.send_lxmf_message_with_method)
# Should try to recall identity first
self.assertIn('Identity.recall(dest_hash)', source)
# Should also try with from_identity_hash=True
self.assertIn('from_identity_hash=True', source)
def test_local_cache_fallback_exists(self):
"""Test that local identities cache is checked"""
import inspect
source = inspect.getsource(reticulum_wrapper.ReticulumWrapper.send_lxmf_message_with_method)
# Should check local cache
self.assertIn('self.identities', source)
if __name__ == '__main__':
unittest.main(verbosity=2)