test_mqtt.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import logging
  2. import unittest.mock
  3. import pytest
  4. from paho.mqtt.client import MQTT_ERR_QUEUE_SIZE, MQTT_ERR_SUCCESS, MQTTMessage, Client
  5. import intertechno_cc1101_mqtt
  6. # pylint: disable=protected-access
  7. @pytest.mark.parametrize("mqtt_host", ["mqtt-broker.local"])
  8. @pytest.mark.parametrize("mqtt_port", [1833])
  9. def test__run(caplog, mqtt_host, mqtt_port):
  10. with unittest.mock.patch(
  11. "paho.mqtt.client.Client"
  12. ) as mqtt_client_mock, caplog.at_level(logging.DEBUG):
  13. intertechno_cc1101_mqtt._run(
  14. mqtt_host=mqtt_host,
  15. mqtt_port=mqtt_port,
  16. mqtt_username=None,
  17. mqtt_password=None,
  18. )
  19. mqtt_client_mock.assert_called_once_with()
  20. assert not mqtt_client_mock().username_pw_set.called
  21. mqtt_client_mock().connect.assert_called_once_with(host=mqtt_host, port=mqtt_port)
  22. mqtt_client_mock().socket().getpeername.return_value = (mqtt_host, mqtt_port)
  23. with caplog.at_level(logging.DEBUG):
  24. mqtt_client_mock().on_connect(mqtt_client_mock(), None, {}, 0)
  25. # pylint: disable=comparison-with-callable
  26. assert mqtt_client_mock().on_message == intertechno_cc1101_mqtt._mqtt_on_message
  27. mqtt_client_mock().subscribe.assert_called_once_with("intertechno-cc1101/+/+/set")
  28. mqtt_client_mock().loop_forever.assert_called_once_with()
  29. assert caplog.record_tuples == [
  30. (
  31. "intertechno_cc1101_mqtt",
  32. logging.INFO,
  33. "connecting to MQTT broker {}:{}".format(mqtt_host, mqtt_port),
  34. ),
  35. (
  36. "intertechno_cc1101_mqtt",
  37. logging.DEBUG,
  38. "connected to MQTT broker {}:{}".format(mqtt_host, mqtt_port),
  39. ),
  40. (
  41. "intertechno_cc1101_mqtt",
  42. logging.INFO,
  43. "subscribing to MQTT topic 'intertechno-cc1101/+/+/set'",
  44. ),
  45. ]
  46. @pytest.mark.parametrize("mqtt_host", ["mqtt-broker.local"])
  47. @pytest.mark.parametrize("mqtt_port", [1833])
  48. @pytest.mark.parametrize("mqtt_username", ["me"])
  49. @pytest.mark.parametrize("mqtt_password", [None, "secret"])
  50. def test__run_authentication(mqtt_host, mqtt_port, mqtt_username, mqtt_password):
  51. with unittest.mock.patch("paho.mqtt.client.Client") as mqtt_client_mock:
  52. intertechno_cc1101_mqtt._run(
  53. mqtt_host=mqtt_host,
  54. mqtt_port=mqtt_port,
  55. mqtt_username=mqtt_username,
  56. mqtt_password=mqtt_password,
  57. )
  58. mqtt_client_mock.assert_called_once_with()
  59. mqtt_client_mock().username_pw_set.assert_called_once_with(
  60. username=mqtt_username, password=mqtt_password
  61. )
  62. @pytest.mark.parametrize("mqtt_host", ["mqtt-broker.local"])
  63. @pytest.mark.parametrize("mqtt_port", [1833])
  64. @pytest.mark.parametrize("mqtt_password", ["secret"])
  65. def test__run_authentication_missing_username(mqtt_host, mqtt_port, mqtt_password):
  66. with unittest.mock.patch("paho.mqtt.client.Client"):
  67. with pytest.raises(ValueError):
  68. intertechno_cc1101_mqtt._run(
  69. mqtt_host=mqtt_host,
  70. mqtt_port=mqtt_port,
  71. mqtt_username=None,
  72. mqtt_password=mqtt_password,
  73. )
  74. @pytest.mark.parametrize(
  75. ("topic", "address"),
  76. (
  77. (b"intertechno-cc1101/12345678/0/set", 12345678),
  78. (b"intertechno-cc1101/1234/0/set", 1234),
  79. ),
  80. )
  81. def test__mqtt_on_message_address(topic, address):
  82. message = MQTTMessage(topic=topic)
  83. message.payload = b"ON"
  84. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  85. intertechno_cc1101_mqtt._mqtt_on_message("dummy", None, message)
  86. remote_control_mock.assert_called_once_with(address=address)
  87. @pytest.mark.parametrize(
  88. ("topic", "address_str"),
  89. (
  90. (b"intertechno-cc1101/abcdef/0/set", "abcdef"),
  91. (b"intertechno-cc1101//0/set", ""),
  92. ),
  93. )
  94. def test__mqtt_on_message_invalid_address(caplog, topic, address_str):
  95. message = MQTTMessage(topic=topic)
  96. message.payload = b"ON"
  97. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  98. with caplog.at_level(logging.WARNING):
  99. intertechno_cc1101_mqtt._mqtt_on_message("dummy", None, message)
  100. remote_control_mock.assert_not_called()
  101. assert caplog.record_tuples == [
  102. (
  103. "intertechno_cc1101_mqtt",
  104. logging.WARNING,
  105. "failed to parse address {!r}, expected integer; ignoring message".format(
  106. address_str
  107. ),
  108. )
  109. ]
  110. @pytest.mark.parametrize(
  111. ("topic", "button_index"),
  112. ((b"intertechno-cc1101/12345678/0/set", 0), (b"intertechno-cc1101/1234/7/set", 7)),
  113. )
  114. @pytest.mark.parametrize(
  115. ("payload", "turn_on"),
  116. ((b"ON", True), (b"On", True), (b"on", True), (b"OFF", False), (b"off", False)),
  117. )
  118. def test__mqtt_on_message_button_index_action(topic, button_index, payload, turn_on):
  119. message = MQTTMessage(topic=topic)
  120. message.payload = payload
  121. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  122. intertechno_cc1101_mqtt._mqtt_on_message("dummy", None, message)
  123. if turn_on:
  124. remote_control_mock().turn_on.assert_called_once_with(button_index=button_index)
  125. remote_control_mock().turn_off.assert_not_called()
  126. else:
  127. remote_control_mock().turn_off.assert_called_once_with(
  128. button_index=button_index
  129. )
  130. remote_control_mock().turn_on.assert_not_called()
  131. @pytest.mark.parametrize(
  132. ("topic", "button_index_str"),
  133. (
  134. (b"intertechno-cc1101/12345678/abc/set", "abc"),
  135. (b"intertechno-cc1101/12345678//set", ""),
  136. ),
  137. )
  138. def test__mqtt_on_message_invalid_button_index(caplog, topic, button_index_str):
  139. message = MQTTMessage(topic=topic)
  140. message.payload = b"ON"
  141. with unittest.mock.patch("intertechno_cc1101.RemoteControl") as remote_control_mock:
  142. with caplog.at_level(logging.WARNING):
  143. intertechno_cc1101_mqtt._mqtt_on_message("dummy", None, message)
  144. remote_control_mock().turn_on.assert_not_called()
  145. remote_control_mock().turn_off.assert_not_called()
  146. assert caplog.record_tuples == [
  147. (
  148. "intertechno_cc1101_mqtt",
  149. logging.WARNING,
  150. "failed to parse button index {!r}, expected integer; ignoring message".format(
  151. button_index_str
  152. ),
  153. )
  154. ]
  155. @pytest.mark.parametrize(
  156. "topic", (b"intertechno-cc1101/123456789/0/set", b"intertechno-cc1101/-21/0/set")
  157. )
  158. def test__mqtt_on_message_remote_init_failed(caplog, topic):
  159. message = MQTTMessage(topic=topic)
  160. message.payload = b"ON"
  161. with caplog.at_level(logging.WARNING):
  162. intertechno_cc1101_mqtt._mqtt_on_message("dummy", None, message)
  163. assert len(caplog.records) == 1
  164. assert caplog.records[0].levelno == logging.WARNING
  165. assert (
  166. caplog.records[0].message
  167. == "failed to initialize remote control, invalid address? ignoring message"
  168. )
  169. assert isinstance(caplog.records[0].exc_info[1], AssertionError)
  170. def test__mqtt_on_message_transmission_failed(caplog):
  171. message = MQTTMessage(topic=b"intertechno-cc1101/12345678/3/set")
  172. message.payload = b"ON"
  173. with unittest.mock.patch(
  174. "cc1101.CC1101.__enter__",
  175. side_effect=FileNotFoundError("[Errno 2] No such file or directory"),
  176. ), caplog.at_level(logging.ERROR):
  177. intertechno_cc1101_mqtt._mqtt_on_message("dummy", None, message)
  178. assert len(caplog.records) == 1
  179. assert caplog.records[0].levelno == logging.ERROR
  180. assert caplog.records[0].message == "failed to send signal"
  181. assert isinstance(caplog.records[0].exc_info[1], FileNotFoundError)