mirror of
https://github.com/torlando-tech/columba.git
synced 2025-12-23 22:20:18 +00:00
When sending to a destination whose identity isn't cached (e.g., peer hasn't announced since app start), automatically call RNS.Transport.request_path() and wait up to 5 seconds for the network to respond with the path. This enables sending messages to known peers without waiting for their periodic announces, improving UX for users resuming conversations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
154 lines
5.9 KiB
Python
154 lines
5.9 KiB
Python
"""
|
|
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)
|