diff --git a/app/keystore.py b/app/keystore.py index 5b2d482..ad5ed28 100644 --- a/app/keystore.py +++ b/app/keystore.py @@ -18,6 +18,13 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) +NO_EVENT_RECEIVED_GUIDANCE = ( + "Radio command channel is unresponsive (no_event_received). Possible causes: " + "incompatible firmware for the meshcore_py command interface; or another " + "serial/TCP/BLE connectivity error. The app cannot proceed because it cannot " + "issue commands to the radio." +) + # In-memory storage for the private key and derived public key _private_key: bytes | None = None _public_key: bytes | None = None @@ -91,8 +98,14 @@ async def export_and_store_private_key(mc: "MeshCore") -> bool: ) return False else: + reason = result.payload.get("reason") if isinstance(result.payload, dict) else None + if result.type == EventType.ERROR and reason == "no_event_received": + logger.error("%s Raw response: %s", NO_EVENT_RECEIVED_GUIDANCE, result.payload) + raise RuntimeError(NO_EVENT_RECEIVED_GUIDANCE) logger.error("Failed to export private key: %s", result.payload) return False + except RuntimeError: + raise except Exception as e: logger.error("Error exporting private key: %s", e) return False diff --git a/tests/test_keystore.py b/tests/test_keystore.py index 08ef558..868c370 100644 --- a/tests/test_keystore.py +++ b/tests/test_keystore.py @@ -147,6 +147,20 @@ class TestExportAndStorePrivateKey: assert result is False assert not has_private_key() + @pytest.mark.asyncio + async def test_no_event_received_raises_runtime_error(self): + """no_event_received indicates command channel failure and should fail setup.""" + mock_mc = MagicMock() + mock_result = MagicMock() + mock_result.type = EventType.ERROR + mock_result.payload = {"reason": "no_event_received"} + mock_mc.commands.export_private_key = AsyncMock(return_value=mock_result) + + with pytest.raises(RuntimeError, match="cannot proceed"): + await export_and_store_private_key(mock_mc) + + assert not has_private_key() + @pytest.mark.asyncio async def test_exception_returns_false(self): """Exception during export returns False without storing."""