diff --git a/tests/backend/conftest.py b/tests/backend/conftest.py index b2bbf70..0da4d53 100644 --- a/tests/backend/conftest.py +++ b/tests/backend/conftest.py @@ -221,6 +221,7 @@ def mock_app(db, tmp_path, temp_db): # a live provider for the same path as the db fixture. app.database = Database(temp_db) app.current_context.config = ConfigManager(app.database) + app.config = app.current_context.config app.websocket_broadcast = MagicMock(side_effect=lambda data: None) yield app diff --git a/tests/backend/fixtures/http_api_routes.json b/tests/backend/fixtures/http_api_routes.json index 9c93829..3c12877 100644 --- a/tests/backend/fixtures/http_api_routes.json +++ b/tests/backend/fixtures/http_api_routes.json @@ -248,6 +248,10 @@ "method": "POST", "path": "/api/v1/favourites/add" }, + { + "method": "POST", + "path": "/api/v1/favourites/import" + }, { "method": "DELETE", "path": "/api/v1/favourites/{destination_hash}" diff --git a/tests/backend/test_interface_discovery.py b/tests/backend/test_interface_discovery.py index 2dd9ad6..c29fff8 100644 --- a/tests/backend/test_interface_discovery.py +++ b/tests/backend/test_interface_discovery.py @@ -82,6 +82,7 @@ async def test_reticulum_discovery_get_and_patch(temp_dir): storage_dir=temp_dir, reticulum_config_dir=temp_dir, ) + app_instance.current_context.config.default_bootstrap_only.set(True) get_handler = await find_route_handler( app_instance, @@ -145,7 +146,8 @@ async def test_reticulum_discovery_get_and_patch(temp_dir): assert "interface_discovery_blacklist" not in config["reticulum"] assert config["reticulum"]["required_discovery_value"] == 18 assert config["reticulum"]["autoconnect_discovered_interfaces"] == 5 - assert config["reticulum"]["default_bootstrap_only"] is False + assert "default_bootstrap_only" not in config["reticulum"] + assert app_instance.current_context.config.default_bootstrap_only.get() is False assert config["reticulum"]["network_identity"] == "/tmp/other_id" assert config.write_called diff --git a/tests/backend/test_lxmf_propagation_sync_integration.py b/tests/backend/test_lxmf_propagation_sync_integration.py index f702e4f..c7af2e0 100644 --- a/tests/backend/test_lxmf_propagation_sync_integration.py +++ b/tests/backend/test_lxmf_propagation_sync_integration.py @@ -61,6 +61,10 @@ class FakePropagationRouter: self.propagation_transfer_state = self.PR_PATH_REQUESTED self.propagation_transfer_progress = 0.0 + def cancel_propagation_node_requests(self): + self.propagation_transfer_state = self.PR_IDLE + self.propagation_transfer_progress = 0.0 + @pytest.fixture def temp_dir(): diff --git a/tests/backend/test_security_fuzzing.py b/tests/backend/test_security_fuzzing.py index ea1f406..8a956b3 100644 --- a/tests/backend/test_security_fuzzing.py +++ b/tests/backend/test_security_fuzzing.py @@ -156,6 +156,23 @@ def mock_app(): app.config.auto_announce_enabled.get.return_value = False app.config.voicemail_enabled.get.return_value = True + def _make_bool_mock(default=False): + mock = MagicMock() + mock.get.return_value = default + + def _set(value): + mock.get.return_value = bool(value) + + mock.set.side_effect = _set + return mock + + app.config.block_all_from_strangers = _make_bool_mock(False) + app.config.block_attachments_from_strangers = _make_bool_mock(False) + app.config.lxmf_flood_protection_enabled.get.return_value = False + app.config.lxmf_flood_threshold_per_minute.get.return_value = 100 + app.config.lxmf_flood_max_stamp_cost.get.return_value = 10 + app.config.lxmf_inbound_stamp_cost.get.return_value = 2 + # Surface mocks for tracking app.is_destination_blocked = MagicMock(return_value=False) app.check_spam_keywords = MagicMock(return_value=False) @@ -2367,8 +2384,8 @@ class TestStrangerAttachmentBlocking: source_hash = os.urandom(16) mock_msg = self._make_mock_message(source_hash=source_hash) - mock_app.config.block_attachments_from_strangers.get.return_value = True - mock_app.config.block_all_from_strangers.get.return_value = False + mock_app.config.block_attachments_from_strangers.set(True) + mock_app.config.block_all_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=False) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2386,8 +2403,8 @@ class TestStrangerAttachmentBlocking: source_hash = os.urandom(16) mock_msg = self._make_mock_message(source_hash=source_hash) - mock_app.config.block_attachments_from_strangers.get.return_value = True - mock_app.config.block_all_from_strangers.get.return_value = False + mock_app.config.block_attachments_from_strangers.set(True) + mock_app.config.block_all_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=True) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2402,8 +2419,8 @@ class TestStrangerAttachmentBlocking: source_hash = os.urandom(16) mock_msg = self._make_mock_message(source_hash=source_hash) - mock_app.config.block_attachments_from_strangers.get.return_value = False - mock_app.config.block_all_from_strangers.get.return_value = False + mock_app.config.block_attachments_from_strangers.set(False) + mock_app.config.block_all_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=False) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2421,8 +2438,8 @@ class TestStrangerAttachmentBlocking: with_attachments=False, ) - mock_app.config.block_attachments_from_strangers.get.return_value = True - mock_app.config.block_all_from_strangers.get.return_value = False + mock_app.config.block_attachments_from_strangers.set(True) + mock_app.config.block_all_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=False) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2456,8 +2473,8 @@ class TestStrangerAttachmentBlocking: mock_msg.get_fields.return_value = fields mock_msg.fields = fields - mock_app.config.block_attachments_from_strangers.get.return_value = True - mock_app.config.block_all_from_strangers.get.return_value = False + mock_app.config.block_attachments_from_strangers.set(True) + mock_app.config.block_all_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=False) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2494,8 +2511,8 @@ class TestStrangerAttachmentBlocking: mock_msg.get_fields.return_value = fields mock_msg.fields = fields - mock_app.config.block_attachments_from_strangers.get.return_value = True - mock_app.config.block_all_from_strangers.get.return_value = False + mock_app.config.block_attachments_from_strangers.set(True) + mock_app.config.block_all_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=True) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2535,8 +2552,8 @@ class TestBlockAllFromStrangers: def test_stranger_message_dropped_when_enabled(self, mock_app): """Text message from a stranger is silently dropped when block_all is on.""" mock_msg = self._make_mock_message() - mock_app.config.block_all_from_strangers.get.return_value = True - mock_app.config.block_attachments_from_strangers.get.return_value = False + mock_app.config.block_all_from_strangers.set(True) + mock_app.config.block_attachments_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=False) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2547,8 +2564,8 @@ class TestBlockAllFromStrangers: def test_contact_message_delivered_when_block_all_enabled(self, mock_app): """Messages from contacts pass through even when block_all is on.""" mock_msg = self._make_mock_message() - mock_app.config.block_all_from_strangers.get.return_value = True - mock_app.config.block_attachments_from_strangers.get.return_value = False + mock_app.config.block_all_from_strangers.set(True) + mock_app.config.block_attachments_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=True) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2559,8 +2576,8 @@ class TestBlockAllFromStrangers: def test_stranger_with_attachments_dropped(self, mock_app): """Message with attachments from stranger is dropped entirely, not just stripped.""" mock_msg = self._make_mock_message(with_attachments=True) - mock_app.config.block_all_from_strangers.get.return_value = True - mock_app.config.block_attachments_from_strangers.get.return_value = True + mock_app.config.block_all_from_strangers.set(True) + mock_app.config.block_attachments_from_strangers.set(True) mock_app._is_contact = MagicMock(return_value=False) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) @@ -2571,8 +2588,8 @@ class TestBlockAllFromStrangers: def test_disabled_allows_stranger_messages(self, mock_app): """When block_all is off, stranger messages are delivered.""" mock_msg = self._make_mock_message() - mock_app.config.block_all_from_strangers.get.return_value = False - mock_app.config.block_attachments_from_strangers.get.return_value = False + mock_app.config.block_all_from_strangers.set(False) + mock_app.config.block_attachments_from_strangers.set(False) mock_app._is_contact = MagicMock(return_value=False) mock_app.is_destination_blocked = MagicMock(return_value=False) mock_app.check_spam_keywords = MagicMock(return_value=False) diff --git a/tests/backend/test_telemetry_integration.py b/tests/backend/test_telemetry_integration.py index 85ea40e..5e5a299 100644 --- a/tests/backend/test_telemetry_integration.py +++ b/tests/backend/test_telemetry_integration.py @@ -34,6 +34,9 @@ def mock_app(): # Mock websocket_broadcast app.websocket_broadcast = MagicMock() + # Required attribute for on_lxmf_delivery flood protection + app._lxmf_incoming_timestamps = [] + # Attach the actual method we want to test if possible, # but since it's an instance method, we might need to bind it. app.process_incoming_telemetry = ( @@ -112,12 +115,13 @@ async def test_telemetry_request_parsing(mock_app): ) mock_app.is_destination_blocked.return_value = False - mock_app.current_context.config.telemetry_enabled.get.return_value = True + mock_app.current_context.config.telemetry_enabled.set(True) mock_app.database.contacts.get_contact_by_identity_hash.return_value = { "is_telemetry_trusted": True, } mock_app.database.messages.get_lxmf_message_by_hash.return_value = {} - mock_app.database.config.get.side_effect = lambda k: 50.0 if "lat" in k else 10.0 + mock_app.database.config.set("map_default_lat", 50.0) + mock_app.database.config.set("map_default_lon", 10.0) mock_app.on_lxmf_delivery(mock_lxmf_message) @@ -142,12 +146,13 @@ async def test_telemetry_request_no_location_does_not_call_handler(mock_app): ) mock_app.is_destination_blocked.return_value = False - mock_app.current_context.config.telemetry_enabled.get.return_value = True + mock_app.current_context.config.telemetry_enabled.set(True) mock_app.database.contacts.get_contact_by_identity_hash.return_value = { "is_telemetry_trusted": True, } mock_app.database.messages.get_lxmf_message_by_hash.return_value = {} - mock_app.database.config.get.return_value = None + mock_app.database.config.set("map_default_lat", None) + mock_app.database.config.set("map_default_lon", None) mock_app.on_lxmf_delivery(mock_lxmf_message) diff --git a/tests/backend/test_websocket_interfaces.py b/tests/backend/test_websocket_interfaces.py index 3888a88..fa2fe8b 100644 --- a/tests/backend/test_websocket_interfaces.py +++ b/tests/backend/test_websocket_interfaces.py @@ -21,8 +21,12 @@ class TestWebsocketInterfaces(unittest.TestCase): self.port = s.getsockname()[1] s.close() + @patch("RNS.Reticulum") @patch("RNS.Interfaces.Interface.Interface.get_config_obj") - def test_server_initialization(self, mock_get_config): + def test_server_initialization(self, mock_get_config, mock_rns): + mock_rns.get_instance.return_value = MagicMock( + _default_ic_max_held_announces=MagicMock(return_value=256), + ) config = { "name": "test_ws_server", "listen_ip": "127.0.0.1", @@ -39,8 +43,12 @@ class TestWebsocketInterfaces(unittest.TestCase): if server.server: server.server.shutdown() + @patch("RNS.Reticulum") @patch("RNS.Interfaces.Interface.Interface.get_config_obj") - def test_client_initialization(self, mock_get_config): + def test_client_initialization(self, mock_get_config, mock_rns): + mock_rns.get_instance.return_value = MagicMock( + _default_ic_max_held_announces=MagicMock(return_value=256), + ) config = {"name": "test_ws_client", "target_url": f"ws://127.0.0.1:{self.port}"} mock_get_config.return_value = config