def _initialize_mqtt_client(mqtt_host, mqtt_port, mqtt_topic_prefix) -> paho.mqtt.client.Client: with unittest.mock.patch("socket.create_connection"), unittest.mock.patch( "ssl.SSLContext.wrap_socket" ) as ssl_wrap_socket_mock, unittest.mock.patch( "paho.mqtt.client.Client.loop_forever", autospec=True) as mqtt_loop_forever_mock, unittest.mock.patch( "gi.repository.GLib.MainLoop.run"), unittest.mock.patch( "systemctl_mqtt._dbus.get_login_manager" ) as get_login_manager_mock: ssl_wrap_socket_mock.return_value.send = len get_login_manager_mock.return_value.Get.return_value = dbus.Boolean( False) systemctl_mqtt._run( mqtt_host=mqtt_host, mqtt_port=mqtt_port, mqtt_username=None, mqtt_password=None, mqtt_topic_prefix=mqtt_topic_prefix, homeassistant_discovery_prefix="discovery-prefix", homeassistant_node_id="node-id", poweroff_delay=datetime.timedelta(), ) while threading.active_count() > 1: time.sleep(0.01) assert mqtt_loop_forever_mock.call_count == 1 (mqtt_client, ) = mqtt_loop_forever_mock.call_args[0] mqtt_client.socket().getpeername.return_value = (mqtt_host, mqtt_port) mqtt_client.on_connect(mqtt_client, mqtt_client._userdata, {}, 0) return mqtt_client
def test__run_authentication(mqtt_host, mqtt_port, mqtt_username, mqtt_password, mqtt_topic_prefix): with unittest.mock.patch("socket.create_connection"), unittest.mock.patch( "ssl.SSLContext.wrap_socket" ) as ssl_wrap_socket_mock, unittest.mock.patch( "paho.mqtt.client.Client.loop_forever", autospec=True) as mqtt_loop_forever_mock, unittest.mock.patch( "gi.repository.GLib.MainLoop.run"), unittest.mock.patch( "systemctl_mqtt._dbus.get_login_manager"): ssl_wrap_socket_mock.return_value.send = len systemctl_mqtt._run( mqtt_host=mqtt_host, mqtt_port=mqtt_port, mqtt_username=mqtt_username, mqtt_password=mqtt_password, mqtt_topic_prefix=mqtt_topic_prefix, homeassistant_discovery_prefix="discovery-prefix", homeassistant_node_id="node-id", poweroff_delay=datetime.timedelta(), ) assert mqtt_loop_forever_mock.call_count == 1 (mqtt_client, ) = mqtt_loop_forever_mock.call_args[0] assert mqtt_client._username.decode() == mqtt_username if mqtt_password: assert mqtt_client._password.decode() == mqtt_password else: assert mqtt_client._password is None
def test__run_tls(caplog, mqtt_host, mqtt_port, mqtt_disable_tls): caplog.set_level(logging.INFO) with unittest.mock.patch("paho.mqtt.client.Client" ) as mqtt_client_class, unittest.mock.patch( "gi.repository.GLib.MainLoop.run"): systemctl_mqtt._run( mqtt_host=mqtt_host, mqtt_port=mqtt_port, mqtt_disable_tls=mqtt_disable_tls, mqtt_username=None, mqtt_password=None, mqtt_topic_prefix="systemctl/hosts", homeassistant_discovery_prefix="homeassistant", homeassistant_node_id="host", poweroff_delay=datetime.timedelta(), ) assert caplog.records[0].levelno == logging.INFO assert caplog.records[0].message == ( "connecting to MQTT broker {}:{} (TLS {})".format( mqtt_host, mqtt_port, "disabled" if mqtt_disable_tls else "enabled")) if mqtt_disable_tls: mqtt_client_class().tls_set.assert_not_called() else: mqtt_client_class().tls_set.assert_called_once_with(ca_certs=None)
def test__run_authentication_missing_username(mqtt_host, mqtt_port, mqtt_password): with unittest.mock.patch("paho.mqtt.client.Client"), unittest.mock.patch( "systemctl_mqtt._dbus.get_login_manager"): with pytest.raises(ValueError, match=r"^Missing MQTT username$"): systemctl_mqtt._run( mqtt_host=mqtt_host, mqtt_port=mqtt_port, mqtt_username=None, mqtt_password=mqtt_password, mqtt_topic_prefix="prefix", homeassistant_discovery_prefix="discovery-prefix", homeassistant_node_id="node-id", poweroff_delay=datetime.timedelta(), )
def test__run_tls_default(): with unittest.mock.patch("paho.mqtt.client.Client" ) as mqtt_client_class, unittest.mock.patch( "gi.repository.GLib.MainLoop.run"): systemctl_mqtt._run( mqtt_host="mqtt-broker.local", mqtt_port=1833, # mqtt_disable_tls default, mqtt_username=None, mqtt_password=None, mqtt_topic_prefix="systemctl/hosts", homeassistant_discovery_prefix="homeassistant", homeassistant_node_id="host", poweroff_delay=datetime.timedelta(), ) # enabled by default mqtt_client_class().tls_set.assert_called_once_with(ca_certs=None)
def test__run( caplog, mqtt_host, mqtt_port, mqtt_topic_prefix, homeassistant_discovery_prefix, homeassistant_node_id, ): # pylint: disable=too-many-locals,too-many-arguments caplog.set_level(logging.DEBUG) with unittest.mock.patch( "socket.create_connection" ) as create_socket_mock, unittest.mock.patch( "ssl.SSLContext.wrap_socket", autospec=True) as ssl_wrap_socket_mock, unittest.mock.patch( "paho.mqtt.client.Client.loop_forever", autospec=True) as mqtt_loop_forever_mock, unittest.mock.patch( "gi.repository.GLib.MainLoop.run" ) as glib_loop_mock, unittest.mock.patch( "systemctl_mqtt._dbus.get_login_manager" ) as get_login_manager_mock: ssl_wrap_socket_mock.return_value.send = len get_login_manager_mock.return_value.Get.return_value = dbus.Boolean( False) systemctl_mqtt._run( mqtt_host=mqtt_host, mqtt_port=mqtt_port, mqtt_username=None, mqtt_password=None, mqtt_topic_prefix=mqtt_topic_prefix, homeassistant_discovery_prefix=homeassistant_discovery_prefix, homeassistant_node_id=homeassistant_node_id, poweroff_delay=datetime.timedelta(), ) assert caplog.records[0].levelno == logging.INFO assert caplog.records[0].message == ( "connecting to MQTT broker {}:{} (TLS enabled)".format( mqtt_host, mqtt_port)) # correct remote? assert create_socket_mock.call_count == 1 create_socket_args, _ = create_socket_mock.call_args assert create_socket_args[0] == (mqtt_host, mqtt_port) # ssl enabled? assert ssl_wrap_socket_mock.call_count == 1 ssl_context = ssl_wrap_socket_mock.call_args[0][0] # self assert ssl_context.check_hostname is True assert ssl_wrap_socket_mock.call_args[1]["server_hostname"] == mqtt_host # loop started? while threading.active_count() > 1: time.sleep(0.01) assert mqtt_loop_forever_mock.call_count == 1 (mqtt_client, ) = mqtt_loop_forever_mock.call_args[0] assert mqtt_client._tls_insecure is False # credentials assert mqtt_client._username is None assert mqtt_client._password is None # connect callback caplog.clear() mqtt_client.socket().getpeername.return_value = (mqtt_host, mqtt_port) with unittest.mock.patch( "paho.mqtt.client.Client.subscribe") as mqtt_subscribe_mock: mqtt_client.on_connect(mqtt_client, mqtt_client._userdata, {}, 0) state = mqtt_client._userdata assert (state._login_manager.connect_to_signal.call_args[1]["signal_name"] == "PrepareForShutdown") assert sorted(mqtt_subscribe_mock.call_args_list) == [ unittest.mock.call(mqtt_topic_prefix + "/lock-all-sessions"), unittest.mock.call(mqtt_topic_prefix + "/poweroff"), ] assert mqtt_client.on_message is None for suffix in ("poweroff", "lock-all-sessions"): assert ( # pylint: disable=comparison-with-callable mqtt_client._on_message_filtered[mqtt_topic_prefix + "/" + suffix] == systemctl_mqtt. _MQTT_TOPIC_SUFFIX_ACTION_MAPPING[suffix].mqtt_message_callback) assert caplog.records[0].levelno == logging.DEBUG assert caplog.records[ 0].message == "connected to MQTT broker {}:{}".format( mqtt_host, mqtt_port) assert caplog.records[1].levelno == logging.DEBUG assert caplog.records[1].message == "acquired shutdown inhibitor lock" assert caplog.records[2].levelno == logging.INFO assert caplog.records[2].message == "publishing 'false' on {}".format( mqtt_topic_prefix + "/preparing-for-shutdown") assert caplog.records[3].levelno == logging.DEBUG assert ( caplog.records[3].message == "publishing home assistant config on " + homeassistant_discovery_prefix + "/binary_sensor/" + homeassistant_node_id + "/preparing-for-shutdown/config") assert all(r.levelno == logging.INFO for r in caplog.records[4::2]) assert {r.message for r in caplog.records[4::2]} == { "subscribing to {}/{}".format(mqtt_topic_prefix, s) for s in ("poweroff", "lock-all-sessions") } assert all(r.levelno == logging.DEBUG for r in caplog.records[5::2]) assert { r.message for r in caplog.records[5::2] } == { "registered MQTT callback for topic {}".format(mqtt_topic_prefix + "/" + s) + " triggering {}".format( systemctl_mqtt._MQTT_TOPIC_SUFFIX_ACTION_MAPPING[s]) for s in ("poweroff", "lock-all-sessions") } # dbus loop started? glib_loop_mock.assert_called_once_with() # waited for mqtt loop to stop? assert mqtt_client._thread_terminate assert mqtt_client._thread is None