mirror of
https://github.com/meshcore-dev/meshcore_py.git
synced 2026-06-11 11:56:18 +00:00
G4: F17 — re-raise BLE pairing failure instead of swallowing
Why: When BLE pairing failed during connect(), the exception was caught as a warning and connect() continued normally. This left the transport in a half-usable state — the BLE link was up but the pairing handshake never completed, so encrypted characteristics could silently fail. Now the pairing exception disconnects the client and re-raises, giving the caller a clean failure. Updated the pre-existing test_ble_connection_with_pin_failed_pairing test to assert the re-raise behavior instead of the old swallow-and-continue behavior. Refs: Forensics report finding F17
This commit is contained in:
@@ -116,9 +116,12 @@ class BLEConnection:
|
|||||||
await self.client.pair()
|
await self.client.pair()
|
||||||
logger.info("BLE pairing successful")
|
logger.info("BLE pairing successful")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"BLE pairing failed: {e}")
|
logger.error(f"BLE pairing failed: {e}")
|
||||||
# Don't fail the connection if pairing fails, as the device
|
# A failed pairing leaves the transport in a half-usable
|
||||||
# might already be paired or not require pairing
|
# state — re-raise so the caller gets a clean failure
|
||||||
|
# instead of a silently degraded connection.
|
||||||
|
await self.client.disconnect()
|
||||||
|
raise
|
||||||
|
|
||||||
except BleakDeviceNotFoundError:
|
except BleakDeviceNotFoundError:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class TestBLEPinPairing(unittest.TestCase):
|
|||||||
|
|
||||||
@patch("meshcore.ble_cx.BleakClient")
|
@patch("meshcore.ble_cx.BleakClient")
|
||||||
def test_ble_connection_with_pin_failed_pairing(self, mock_bleak_client):
|
def test_ble_connection_with_pin_failed_pairing(self, mock_bleak_client):
|
||||||
"""Test BLE connection with PIN when pairing fails but connection continues"""
|
"""Test BLE connection with PIN when pairing fails — re-raises (F17)."""
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_client_instance = self._get_mock_bleak_client()
|
mock_client_instance = self._get_mock_bleak_client()
|
||||||
mock_client_instance.pair = AsyncMock(side_effect=Exception("Pairing failed"))
|
mock_client_instance.pair = AsyncMock(side_effect=Exception("Pairing failed"))
|
||||||
@@ -47,17 +47,16 @@ class TestBLEPinPairing(unittest.TestCase):
|
|||||||
pin = "123456"
|
pin = "123456"
|
||||||
ble_conn = BLEConnection(address=address, pin=pin)
|
ble_conn = BLEConnection(address=address, pin=pin)
|
||||||
|
|
||||||
# Act
|
# Act & Assert — pairing failure now re-raises instead of being
|
||||||
result = asyncio.run(ble_conn.connect())
|
# swallowed, because a half-usable transport is worse than a clean
|
||||||
|
# failure (forensics finding F17).
|
||||||
# Assert
|
with self.assertRaises(Exception) as ctx:
|
||||||
|
asyncio.run(ble_conn.connect())
|
||||||
|
self.assertIn("Pairing failed", str(ctx.exception))
|
||||||
mock_client_instance.connect.assert_called_once()
|
mock_client_instance.connect.assert_called_once()
|
||||||
mock_client_instance.pair.assert_called_once()
|
mock_client_instance.pair.assert_called_once()
|
||||||
mock_client_instance.start_notify.assert_called_once_with(
|
# disconnect should be called to clean up the failed connection
|
||||||
UART_TX_CHAR_UUID, ble_conn.handle_rx
|
mock_client_instance.disconnect.assert_called_once()
|
||||||
)
|
|
||||||
# Connection should still succeed even if pairing fails
|
|
||||||
self.assertEqual(result, address)
|
|
||||||
|
|
||||||
@patch("meshcore.ble_cx.BleakClient")
|
@patch("meshcore.ble_cx.BleakClient")
|
||||||
def test_ble_connection_without_pin_no_pairing(self, mock_bleak_client):
|
def test_ble_connection_without_pin_no_pairing(self, mock_bleak_client):
|
||||||
|
|||||||
Reference in New Issue
Block a user