test_short_mfr_guards.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. """Regression tests: parsers must not raise IndexError on short mfr_data (#494)."""
  2. from __future__ import annotations
  3. import pytest
  4. from switchbot.adv_parsers.contact import process_wocontact
  5. from switchbot.adv_parsers.leak import process_leak
  6. from switchbot.adv_parsers.presence_sensor import process_presence_sensor
  7. @pytest.mark.parametrize(
  8. "mfr_data",
  9. [b"", b"\x00", b"\x00\x01", b"\x00" * 8],
  10. )
  11. def test_process_leak_short_mfr_returns_empty(mfr_data: bytes) -> None:
  12. """Leak parser must not crash when mfr_data is shorter than 9 bytes."""
  13. data = b"&\x00N"
  14. assert process_leak(data, mfr_data) == {}
  15. @pytest.mark.parametrize(
  16. "mfr_data",
  17. [b"", b"\x00" * 6, b"\x00" * 11],
  18. )
  19. def test_process_presence_sensor_short_mfr_returns_empty(mfr_data: bytes) -> None:
  20. """Presence sensor parser must not crash when mfr_data is shorter than 12 bytes."""
  21. assert process_presence_sensor(None, mfr_data) == {}
  22. def test_process_presence_sensor_none_mfr_returns_empty() -> None:
  23. assert process_presence_sensor(None, None) == {}
  24. @pytest.mark.parametrize(
  25. ("data", "mfr_data"),
  26. [
  27. (None, b""),
  28. (None, b"\x00" * 12),
  29. (b"", None),
  30. (b"\x00" * 8, None),
  31. (b"\x00", b"\x00" * 12),
  32. (b"\x00" * 8, b"\x00" * 12),
  33. ],
  34. )
  35. def test_process_wocontact_short_payloads_return_empty(
  36. data: bytes | None, mfr_data: bytes | None
  37. ) -> None:
  38. """Contact parser must not crash when neither payload is long enough."""
  39. assert process_wocontact(data, mfr_data) == {}
  40. def test_process_wocontact_uses_mfr_when_data_short() -> None:
  41. """Long mfr_data lets the parser succeed even with short service data."""
  42. # mfr_data byte 7 = 0xF0 -> motion+light+contact_open+contact_timeout all True
  43. mfr_data = b"\x00\x00\x00\x00\x00\x00\x00\xf0\x00\x00\x00\x00\x02"
  44. result = process_wocontact(None, mfr_data)
  45. assert result["motion_detected"] is True
  46. assert result["contact_open"] is True
  47. assert result["contact_timeout"] is True
  48. assert result["is_light"] is True
  49. assert result["button_count"] == 2
  50. assert result["battery"] is None
  51. assert result["tested"] is None
  52. def test_process_wocontact_uses_data_when_mfr_missing() -> None:
  53. """Long service data lets the parser succeed without mfr_data."""
  54. data = b"d@d\x05\x00u\x00\xf8\x12"
  55. result = process_wocontact(data, None)
  56. assert result["battery"] == 100
  57. assert result["motion_detected"] is True
  58. assert result["contact_open"] is True
  59. assert result["contact_timeout"] is True
  60. assert result["is_light"] is True
  61. assert result["button_count"] == 2