test_mqtt_message.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import logging
  2. import unittest.mock
  3. import pytest
  4. from paho.mqtt.client import MQTTMessage
  5. import intertechno_cc1101_mqtt
  6. # pylint: disable=protected-access
  7. def test__mqtt_on_message_retained(caplog):
  8. message = MQTTMessage(topic=b"intertechno-cc1101/12345678/0/set")
  9. message.payload = b"ON"
  10. message.retain = True
  11. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  12. with caplog.at_level(logging.DEBUG):
  13. intertechno_cc1101_mqtt._mqtt_on_message("dummy", {}, message)
  14. remote_control_mock.assert_not_called()
  15. assert caplog.record_tuples == [
  16. (
  17. "intertechno_cc1101_mqtt",
  18. logging.DEBUG,
  19. "received topic=intertechno-cc1101/12345678/0/set payload=b'ON'",
  20. ),
  21. ("intertechno_cc1101_mqtt", logging.WARNING, "ignoring retained message"),
  22. ]
  23. @pytest.mark.parametrize(
  24. ("topic", "address"),
  25. (
  26. (b"intertechno-cc1101/12345678/0/set", 12345678),
  27. (b"intertechno-cc1101/1234/0/set", 1234),
  28. ),
  29. )
  30. def test__mqtt_on_message_address(topic, address):
  31. message = MQTTMessage(topic=topic)
  32. message.payload = b"ON"
  33. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  34. intertechno_cc1101_mqtt._mqtt_on_message("dummy", {}, message)
  35. remote_control_mock.assert_called_once_with(address=address)
  36. @pytest.mark.parametrize(
  37. ("topic", "address_str"),
  38. (
  39. (b"intertechno-cc1101/abcdef/0/set", "abcdef"),
  40. (b"intertechno-cc1101//0/set", ""),
  41. ),
  42. )
  43. def test__mqtt_on_message_invalid_address(caplog, topic, address_str):
  44. message = MQTTMessage(topic=topic)
  45. message.payload = b"ON"
  46. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  47. with caplog.at_level(logging.WARNING):
  48. intertechno_cc1101_mqtt._mqtt_on_message("dummy", {}, message)
  49. remote_control_mock.assert_not_called()
  50. assert caplog.record_tuples == [
  51. (
  52. "intertechno_cc1101_mqtt",
  53. logging.WARNING,
  54. "failed to parse address {!r}, expected integer; ignoring message".format(
  55. address_str
  56. ),
  57. )
  58. ]
  59. @pytest.mark.parametrize(
  60. ("topic", "button_index"),
  61. ((b"intertechno-cc1101/12345678/0/set", 0), (b"intertechno-cc1101/1234/7/set", 7)),
  62. )
  63. @pytest.mark.parametrize(
  64. ("payload", "turn_on"),
  65. ((b"ON", True), (b"On", True), (b"on", True), (b"OFF", False), (b"off", False)),
  66. )
  67. def test__mqtt_on_message_button_index_action(topic, button_index, payload, turn_on):
  68. message = MQTTMessage(topic=topic)
  69. message.payload = payload
  70. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  71. intertechno_cc1101_mqtt._mqtt_on_message("dummy", {}, message)
  72. if turn_on:
  73. remote_control_mock().turn_on.assert_called_once_with(button_index=button_index)
  74. remote_control_mock().turn_off.assert_not_called()
  75. else:
  76. remote_control_mock().turn_off.assert_called_once_with(
  77. button_index=button_index
  78. )
  79. remote_control_mock().turn_on.assert_not_called()
  80. @pytest.mark.parametrize(
  81. ("topic", "button_index_str"),
  82. (
  83. (b"intertechno-cc1101/12345678/abc/set", "abc"),
  84. (b"intertechno-cc1101/12345678//set", ""),
  85. ),
  86. )
  87. def test__mqtt_on_message_invalid_button_index(caplog, topic, button_index_str):
  88. message = MQTTMessage(topic=topic)
  89. message.payload = b"ON"
  90. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  91. with caplog.at_level(logging.WARNING):
  92. intertechno_cc1101_mqtt._mqtt_on_message("dummy", {}, message)
  93. remote_control_mock().turn_on.assert_not_called()
  94. remote_control_mock().turn_off.assert_not_called()
  95. assert caplog.record_tuples == [
  96. (
  97. "intertechno_cc1101_mqtt",
  98. logging.WARNING,
  99. "failed to parse button index {!r}, expected integer; ignoring message".format(
  100. button_index_str
  101. ),
  102. )
  103. ]
  104. @pytest.mark.parametrize(
  105. ("topic", "address", "button_index"),
  106. (
  107. (b"intertechno-cc1101/some-name/set", 21, 0),
  108. (b"intertechno-cc1101/another-name/set", 42, 7),
  109. ),
  110. )
  111. def test__mqtt_on_message_alias(topic, address, button_index):
  112. message = MQTTMessage(topic=topic)
  113. message.payload = b"ON"
  114. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  115. intertechno_cc1101_mqtt._mqtt_on_message(
  116. "dummy",
  117. {
  118. "some-name": {"address": 21, "button-index": 0},
  119. "another-name": {"address": "42", "button-index": "7"}, # :O
  120. },
  121. message,
  122. )
  123. remote_control_mock.assert_called_once_with(address=address)
  124. remote_control_mock().turn_on.assert_called_once_with(button_index=button_index)
  125. @pytest.mark.parametrize(
  126. ("topic", "alias"),
  127. (
  128. (b"intertechno-cc1101//set", ""),
  129. (b"intertechno-cc1101/unknown-name/set", "unknown-name"),
  130. ),
  131. )
  132. def test__mqtt_on_message_undefined_alias(caplog, topic, alias):
  133. message = MQTTMessage(topic=topic)
  134. message.payload = b"ON"
  135. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  136. with caplog.at_level(logging.WARNING):
  137. intertechno_cc1101_mqtt._mqtt_on_message(
  138. "dummy", {"some-name": {"address": 21, "button-index": 0}}, message
  139. )
  140. remote_control_mock.assert_not_called()
  141. assert caplog.record_tuples == [
  142. (
  143. "intertechno_cc1101_mqtt",
  144. logging.WARNING,
  145. "unknown alias {!r}; ignoring message".format(alias),
  146. )
  147. ]
  148. @pytest.mark.parametrize(
  149. "aliases",
  150. (
  151. {"some-name": {"address": 21}},
  152. {"some-name": {"button-index": 0}},
  153. {"some-name": {"adresse": 21, "button-index": 0}},
  154. ),
  155. )
  156. def test__mqtt_on_message_alias_missing_attrs(caplog, aliases):
  157. message = MQTTMessage(topic=b"intertechno-cc1101/some-name/set")
  158. message.payload = b"ON"
  159. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  160. with caplog.at_level(logging.WARNING):
  161. intertechno_cc1101_mqtt._mqtt_on_message("dummy", aliases, message)
  162. remote_control_mock.assert_not_called()
  163. assert caplog.record_tuples == [
  164. (
  165. "intertechno_cc1101_mqtt",
  166. logging.ERROR,
  167. "alias file must provide fields 'address' and 'button-index' for each alias",
  168. )
  169. ]
  170. @pytest.mark.parametrize(
  171. "topic", (b"intertechno-cc1101/123456789/0/set", b"intertechno-cc1101/-21/0/set")
  172. )
  173. def test__mqtt_on_message_remote_init_failed(caplog, topic):
  174. message = MQTTMessage(topic=topic)
  175. message.payload = b"ON"
  176. with caplog.at_level(logging.WARNING):
  177. intertechno_cc1101_mqtt._mqtt_on_message("dummy", {}, message)
  178. assert len(caplog.records) == 1
  179. assert caplog.records[0].levelno == logging.WARNING
  180. assert (
  181. caplog.records[0].message
  182. == "failed to initialize remote control, invalid address? ignoring message"
  183. )
  184. assert isinstance(caplog.records[0].exc_info[1], AssertionError)
  185. def test__mqtt_on_message_transmission_failed(caplog):
  186. message = MQTTMessage(topic=b"intertechno-cc1101/12345678/3/set")
  187. message.payload = b"ON"
  188. with unittest.mock.patch(
  189. "cc1101.CC1101.__enter__",
  190. side_effect=FileNotFoundError("[Errno 2] No such file or directory"),
  191. ), caplog.at_level(logging.ERROR):
  192. intertechno_cc1101_mqtt._mqtt_on_message("dummy", {}, message)
  193. assert len(caplog.records) == 1
  194. assert caplog.records[0].levelno == logging.ERROR
  195. assert caplog.records[0].message == "failed to send signal"
  196. assert isinstance(caplog.records[0].exc_info[1], FileNotFoundError)
  197. @pytest.mark.parametrize("payload", (b"EIN", b"aus", b""))
  198. def test__mqtt_on_message_invalid_payload(caplog, payload):
  199. message = MQTTMessage(topic=b"intertechno-cc1101/1234/7/set")
  200. message.payload = payload
  201. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  202. intertechno_cc1101_mqtt._mqtt_on_message("dummy", {}, message)
  203. remote_control_mock().turn_off.assert_not_called()
  204. remote_control_mock().turn_on.assert_not_called()
  205. assert caplog.record_tuples == [
  206. (
  207. "intertechno_cc1101_mqtt",
  208. 30,
  209. "unexpected payload {!r}; expected 'ON' or 'OFF'".format(payload),
  210. )
  211. ]