Explorar o código

refactor: add attr poweroff_delay to state class

Fabian Peter Hammerle %!s(int64=3) %!d(string=hai) anos
pai
achega
9bb7a600ff
Modificáronse 6 ficheiros con 71 adicións e 18 borrados
  1. 15 12
      systemctl_mqtt/__init__.py
  2. 27 0
      tests/test_action.py
  3. 4 1
      tests/test_cli.py
  4. 8 1
      tests/test_dbus.py
  5. 11 4
      tests/test_mqtt.py
  6. 6 0
      tests/test_state_dbus.py

+ 15 - 12
systemctl_mqtt/__init__.py

@@ -48,9 +48,11 @@ _LOGGER = logging.getLogger(__name__)
 class _State:
     def __init__(
         self,
+        *,
         mqtt_topic_prefix: str,
         homeassistant_discovery_prefix: str,
         homeassistant_node_id: str,
+        poweroff_delay: datetime.timedelta,
     ) -> None:
         self._mqtt_topic_prefix = mqtt_topic_prefix
         self._homeassistant_discovery_prefix = homeassistant_discovery_prefix
@@ -60,6 +62,7 @@ class _State:
         )  # type: dbus.proxies.Interface
         self._shutdown_lock = None  # type: typing.Optional[dbus.types.UnixFd]
         self._shutdown_lock_mutex = threading.Lock()
+        self.poweroff_delay = poweroff_delay
 
     @property
     def mqtt_topic_prefix(self) -> str:
@@ -195,8 +198,8 @@ class _State:
 
 class _MQTTAction(metaclass=abc.ABCMeta):
     @abc.abstractmethod
-    def trigger(self) -> None:
-        pass
+    def trigger(self, state: _State) -> None:
+        pass # pragma: no cover
 
     def mqtt_message_callback(
         self,
@@ -212,26 +215,22 @@ class _MQTTAction(metaclass=abc.ABCMeta):
             _LOGGER.info("ignoring retained message")
             return
         _LOGGER.debug("executing action %s", self)
-        self.trigger()
+        self.trigger(state=state)
         _LOGGER.debug("completed action %s", self)
 
 
 class _MQTTActionSchedulePoweroff(_MQTTAction):
-    def __init__(self, delay: datetime.timedelta) -> None:
-        super().__init__()
-        self._delay = delay
-
-    def trigger(self) -> None:
+    def trigger(self, state: _State) -> None:
         # pylint: disable=protected-access
-        systemctl_mqtt._dbus.schedule_shutdown(action="poweroff", delay=self._delay)
+        systemctl_mqtt._dbus.schedule_shutdown(
+            action="poweroff", delay=state.poweroff_delay
+        )
 
     def __str__(self) -> str:
         return type(self).__name__
 
 
-_MQTT_TOPIC_SUFFIX_ACTION_MAPPING = {
-    "poweroff": _MQTTActionSchedulePoweroff(delay=datetime.timedelta(seconds=4))
-}
+_MQTT_TOPIC_SUFFIX_ACTION_MAPPING = {"poweroff": _MQTTActionSchedulePoweroff()}
 
 
 def _mqtt_on_connect(
@@ -263,6 +262,7 @@ def _mqtt_on_connect(
 
 
 def _run(
+    *,
     mqtt_host: str,
     mqtt_port: int,
     mqtt_username: typing.Optional[str],
@@ -270,6 +270,7 @@ def _run(
     mqtt_topic_prefix: str,
     homeassistant_discovery_prefix: str,
     homeassistant_node_id: str,
+    poweroff_delay: datetime.timedelta,
     mqtt_disable_tls: bool = False,
 ) -> None:
     # pylint: disable=too-many-arguments
@@ -281,6 +282,7 @@ def _run(
             mqtt_topic_prefix=mqtt_topic_prefix,
             homeassistant_discovery_prefix=homeassistant_discovery_prefix,
             homeassistant_node_id=homeassistant_node_id,
+            poweroff_delay=poweroff_delay,
         )
     )
     mqtt_client.on_connect = _mqtt_on_connect
@@ -393,4 +395,5 @@ def _main() -> None:
         mqtt_topic_prefix=args.mqtt_topic_prefix,
         homeassistant_discovery_prefix=args.homeassistant_discovery_prefix,
         homeassistant_node_id=args.homeassistant_node_id,
+        poweroff_delay=datetime.timedelta(seconds=4),
     )

+ 27 - 0
tests/test_action.py

@@ -0,0 +1,27 @@
+import datetime
+import unittest.mock
+
+import pytest
+
+import systemctl_mqtt
+
+# pylint: disable=protected-access
+
+
+@pytest.mark.parametrize(
+    "delay", [datetime.timedelta(seconds=4), datetime.timedelta(hours=21)]
+)
+def test_poweroff_trigger(delay):
+    action = systemctl_mqtt._MQTTActionSchedulePoweroff()
+    with unittest.mock.patch(
+        "systemctl_mqtt._dbus.schedule_shutdown"
+    ) as schedule_shutdown_mock:
+        action.trigger(
+            state=systemctl_mqtt._State(
+                mqtt_topic_prefix="systemctl/hostname",
+                homeassistant_discovery_prefix="homeassistant",
+                homeassistant_node_id="node",
+                poweroff_delay=delay,
+            )
+        )
+    schedule_shutdown_mock.assert_called_once_with(action="poweroff", delay=delay)

+ 4 - 1
tests/test_cli.py

@@ -15,6 +15,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
+import datetime
 import typing
 import unittest.mock
 
@@ -159,11 +160,12 @@ def test__main(
         mqtt_topic_prefix=expected_topic_prefix or "systemctl/hostname",
         homeassistant_discovery_prefix="homeassistant",
         homeassistant_node_id="hostname",
+        poweroff_delay=datetime.timedelta(seconds=4),
     )
 
 
 @pytest.mark.parametrize(
-    ("password_file_content", "expected_password",),
+    ("password_file_content", "expected_password"),
     [
         ("secret", "secret"),
         ("secret space", "secret space"),
@@ -205,6 +207,7 @@ def test__main_password_file(tmpdir, password_file_content, expected_password):
         mqtt_topic_prefix="systemctl/hostname",
         homeassistant_discovery_prefix="homeassistant",
         homeassistant_node_id="hostname",
+        poweroff_delay=datetime.timedelta(seconds=4),
     )
 
 

+ 8 - 1
tests/test_dbus.py

@@ -160,7 +160,14 @@ def test_mqtt_topic_suffix_action_mapping(topic_suffix, expected_action_arg):
     with unittest.mock.patch(
         "systemctl_mqtt._dbus.get_login_manager", return_value=login_manager_mock,
     ):
-        mqtt_action.trigger()
+        mqtt_action.trigger(
+            state=systemctl_mqtt._State(
+                mqtt_topic_prefix="systemctl/hostname",
+                homeassistant_discovery_prefix="homeassistant",
+                homeassistant_node_id="node",
+                poweroff_delay=datetime.timedelta(),
+            )
+        )
     assert login_manager_mock.ScheduleShutdown.call_count == 1
     schedule_args, schedule_kwargs = login_manager_mock.ScheduleShutdown.call_args
     assert len(schedule_args) == 2

+ 11 - 4
tests/test_mqtt.py

@@ -15,6 +15,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
+import datetime
 import logging
 import threading
 import time
@@ -66,6 +67,7 @@ def test__run(
             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 == (
@@ -162,6 +164,7 @@ def test__run_tls(caplog, mqtt_host, mqtt_port, mqtt_disable_tls):
             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 == (
@@ -188,6 +191,7 @@ def test__run_tls_default():
             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)
@@ -219,6 +223,7 @@ def test__run_authentication(
             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]
@@ -251,6 +256,7 @@ def _initialize_mqtt_client(
             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)
@@ -275,7 +281,7 @@ def test__client_handle_message(caplog, mqtt_host, mqtt_port, mqtt_topic_prefix)
         systemctl_mqtt._MQTT_TOPIC_SUFFIX_ACTION_MAPPING["poweroff"], "trigger"
     ) as poweroff_trigger_mock:
         mqtt_client._handle_on_message(poweroff_message)
-    poweroff_trigger_mock.assert_called_once_with()
+    poweroff_trigger_mock.assert_called_once_with(state=mqtt_client._userdata)
     assert all(r.levelno == logging.DEBUG for r in caplog.records)
     assert caplog.records[0].message == "received topic={} payload=b''".format(
         poweroff_message.topic
@@ -291,7 +297,7 @@ def test__run_authentication_missing_username(mqtt_host, mqtt_port, mqtt_passwor
     with unittest.mock.patch("paho.mqtt.client.Client"), unittest.mock.patch(
         "systemctl_mqtt._dbus.get_login_manager"
     ):
-        with pytest.raises(ValueError):
+        with pytest.raises(ValueError, match=r"^Missing MQTT username$"):
             systemctl_mqtt._run(
                 mqtt_host=mqtt_host,
                 mqtt_port=mqtt_port,
@@ -300,6 +306,7 @@ def test__run_authentication_missing_username(mqtt_host, mqtt_port, mqtt_passwor
                 mqtt_topic_prefix="prefix",
                 homeassistant_discovery_prefix="discovery-prefix",
                 homeassistant_node_id="node-id",
+                poweroff_delay=datetime.timedelta(),
             )
 
 
@@ -314,9 +321,9 @@ def test_mqtt_message_callback_poweroff(caplog, mqtt_topic: str, payload: bytes)
         systemctl_mqtt._MQTT_TOPIC_SUFFIX_ACTION_MAPPING[
             "poweroff"
         ].mqtt_message_callback(
-            None, None, message  # type: ignore
+            None, "state_dummy", message  # type: ignore
         )
-    trigger_mock.assert_called_once_with()
+    trigger_mock.assert_called_once_with(state="state_dummy")
     assert len(caplog.records) == 3
     assert caplog.records[0].levelno == logging.DEBUG
     assert caplog.records[0].message == (

+ 6 - 0
tests/test_state_dbus.py

@@ -15,6 +15,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
+import datetime
 import json
 import logging
 import unittest.mock
@@ -34,6 +35,7 @@ def test_shutdown_lock():
             mqtt_topic_prefix="any",
             homeassistant_discovery_prefix=None,
             homeassistant_node_id=None,
+            poweroff_delay=datetime.timedelta(),
         )
         state._login_manager.Inhibit.return_value = lock_fd
         state.acquire_shutdown_lock()
@@ -55,6 +57,7 @@ def test_prepare_for_shutdown_handler(caplog, active):
             mqtt_topic_prefix="any",
             homeassistant_discovery_prefix=None,
             homeassistant_node_id=None,
+            poweroff_delay=datetime.timedelta(),
         )
     mqtt_client_mock = unittest.mock.MagicMock()
     state.register_prepare_for_shutdown_handler(mqtt_client=mqtt_client_mock)
@@ -98,6 +101,7 @@ def test_publish_preparing_for_shutdown(active):
             mqtt_topic_prefix="any",
             homeassistant_discovery_prefix=None,
             homeassistant_node_id=None,
+            poweroff_delay=datetime.timedelta(),
         )
     assert state._login_manager == login_manager_mock
     mqtt_client_mock = unittest.mock.MagicMock()
@@ -124,6 +128,7 @@ def test_publish_preparing_for_shutdown_get_fail(caplog):
             mqtt_topic_prefix="any",
             homeassistant_discovery_prefix=None,
             homeassistant_node_id=None,
+            poweroff_delay=datetime.timedelta(),
         )
     mqtt_client_mock = unittest.mock.MagicMock()
     state.publish_preparing_for_shutdown(mqtt_client=None)
@@ -147,6 +152,7 @@ def test_publish_preparing_for_shutdown_homeassistant_config(
         mqtt_topic_prefix=topic_prefix,
         homeassistant_discovery_prefix=discovery_prefix,
         homeassistant_node_id=node_id,
+        poweroff_delay=datetime.timedelta(),
     )
     mqtt_client = unittest.mock.MagicMock()
     with unittest.mock.patch(