Fix ble_alerter: MAC name filter, Matrix 429 backoff, active scanner
- Reject dash/colon MAC-format device names (e.g. 45-48-39-7F-73-C7)
from open mode — were slipping past is_generic_name()
- Add exponential backoff (1s, 2s) on Matrix 429/5xx, max 3 attempts —
startup burst was rate-limiting and dropping alerts
- Commit active scanner flag (scanning_mode=active) that was in working
tree but not in prior commit 8686eb5 despite the commit message
- Add 3 tests: MAC format filter, 429 retry success, 429 give-up
Fixes DevTrack #518
This commit is contained in:
@@ -55,6 +55,14 @@ class TestIsGenericName(unittest.TestCase):
|
||||
self.assertTrue(ble_alerter.is_generic_name("Unknown Device"))
|
||||
self.assertFalse(ble_alerter.is_generic_name("John's iPhone"))
|
||||
|
||||
def test_mac_format_names(self):
|
||||
"""MAC-format names (dash or colon separated) are generic."""
|
||||
self.assertTrue(ble_alerter.is_generic_name("45-48-39-7F-73-C7"))
|
||||
self.assertTrue(ble_alerter.is_generic_name("45:48:39:7F:73:C7"))
|
||||
self.assertTrue(ble_alerter.is_generic_name("aa:bb:cc:dd:ee:ff"))
|
||||
# Partial MAC (not 6 groups) is NOT caught by regex — falls through to other checks
|
||||
self.assertTrue(ble_alerter.is_generic_name("AABBCC")) # caught by all-hex check
|
||||
|
||||
def test_real_device_names(self):
|
||||
"""Real device names are not generic."""
|
||||
self.assertFalse(ble_alerter.is_generic_name("John's iPhone"))
|
||||
@@ -104,16 +112,50 @@ class TestSendAlert(unittest.TestCase):
|
||||
"""Alert is sent to Matrix API."""
|
||||
ble_alerter.MATRIX_ACCESS_TOKEN = "syt_test"
|
||||
ble_alerter.MATRIX_ROOM_ID = "!abc:m.test.com"
|
||||
|
||||
|
||||
mock_urlopen.return_value.__enter__ = MagicMock(return_value=MagicMock())
|
||||
mock_urlopen.return_value.__exit__ = MagicMock(return_value=False)
|
||||
|
||||
|
||||
ble_alerter.send_alert("[BLE] ARRIVED: test")
|
||||
|
||||
|
||||
mock_urlopen.assert_called_once()
|
||||
args, kwargs = mock_urlopen.call_args
|
||||
self.assertIn("_matrix/client/v3/rooms", args[0].full_url)
|
||||
|
||||
@patch('ble_alerter.time.sleep')
|
||||
@patch('ble_alerter.urllib.request.urlopen')
|
||||
def test_send_alert_retries_on_429(self, mock_urlopen, mock_sleep):
|
||||
"""429 response triggers retry with backoff, succeeds on second attempt."""
|
||||
import urllib.error
|
||||
ble_alerter.MATRIX_ACCESS_TOKEN = "syt_test"
|
||||
ble_alerter.MATRIX_ROOM_ID = "!abc:m.test.com"
|
||||
|
||||
# First call raises 429, second succeeds
|
||||
mock_urlopen.side_effect = [
|
||||
urllib.error.HTTPError(None, 429, "Too Many Requests", {}, None),
|
||||
MagicMock(__enter__=MagicMock(return_value=MagicMock()), __exit__=MagicMock(return_value=False)),
|
||||
]
|
||||
|
||||
ble_alerter.send_alert("[BLE] test")
|
||||
|
||||
self.assertEqual(mock_urlopen.call_count, 2)
|
||||
mock_sleep.assert_called_once_with(1) # 2^0 = 1s backoff
|
||||
|
||||
@patch('ble_alerter.time.sleep')
|
||||
@patch('ble_alerter.urllib.request.urlopen')
|
||||
def test_send_alert_gives_up_after_3_attempts(self, mock_urlopen, mock_sleep):
|
||||
"""Persistent 429 gives up after 3 attempts."""
|
||||
import urllib.error
|
||||
ble_alerter.MATRIX_ACCESS_TOKEN = "syt_test"
|
||||
ble_alerter.MATRIX_ROOM_ID = "!abc:m.test.com"
|
||||
|
||||
mock_urlopen.side_effect = urllib.error.HTTPError(None, 429, "Too Many Requests", {}, None)
|
||||
|
||||
ble_alerter.send_alert("[BLE] test")
|
||||
|
||||
self.assertEqual(mock_urlopen.call_count, 3)
|
||||
self.assertEqual(mock_sleep.call_count, 2) # sleeps after attempt 0 and 1
|
||||
|
||||
|
||||
class TestArrivalDeparture(unittest.IsolatedAsyncioTestCase):
|
||||
"""Test arrival/departure detection."""
|
||||
|
||||
Reference in New Issue
Block a user