Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ repos:
language: system
types:
- python
pass_filenames: false
require_serial: true
- id: pytest
name: Run pytest
Expand Down
16 changes: 12 additions & 4 deletions src/publisher/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,27 @@ def publish_json(
raise NotImplementedError

@abstractmethod
def publish_str(self, key: str, value: str, no_prefix: bool = False) -> None:
def publish_str(
self, key: str, value: str, no_prefix: bool = False, *, retain: bool = True
) -> None:
raise NotImplementedError

@abstractmethod
def publish_int(self, key: str, value: int, no_prefix: bool = False) -> None:
def publish_int(
self, key: str, value: int, no_prefix: bool = False, *, retain: bool = True
) -> None:
raise NotImplementedError

@abstractmethod
def publish_bool(self, key: str, value: bool, no_prefix: bool = False) -> None:
def publish_bool(
self, key: str, value: bool, no_prefix: bool = False, *, retain: bool = True
) -> None:
raise NotImplementedError

@abstractmethod
def publish_float(self, key: str, value: float, no_prefix: bool = False) -> None:
def publish_float(
self, key: str, value: float, no_prefix: bool = False, *, retain: bool = True
) -> None:
raise NotImplementedError

@abstractmethod
Expand Down
30 changes: 19 additions & 11 deletions src/publisher/log_publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,35 @@ def publish_json(
retain: bool = True,
) -> None:
anonymized_json = self.dict_to_anonymized_json(data)
self.internal_publish(key, anonymized_json)
self.internal_publish(key, anonymized_json, retain=retain)

@override
def publish_str(self, key: str, value: str, no_prefix: bool = False) -> None:
self.internal_publish(key, value)
def publish_str(
self, key: str, value: str, no_prefix: bool = False, *, retain: bool = True
) -> None:
self.internal_publish(key, value, retain=retain)

@override
def publish_int(self, key: str, value: int, no_prefix: bool = False) -> None:
self.internal_publish(key, value)
def publish_int(
self, key: str, value: int, no_prefix: bool = False, *, retain: bool = True
) -> None:
self.internal_publish(key, value, retain=retain)

@override
def publish_bool(self, key: str, value: bool, no_prefix: bool = False) -> None:
self.internal_publish(key, value)
def publish_bool(
self, key: str, value: bool, no_prefix: bool = False, *, retain: bool = True
) -> None:
self.internal_publish(key, value, retain=retain)

@override
def publish_float(self, key: str, value: float, no_prefix: bool = False) -> None:
self.internal_publish(key, value)
def publish_float(
self, key: str, value: float, no_prefix: bool = False, *, retain: bool = True
) -> None:
self.internal_publish(key, value, retain=retain)

@override
def clear_topic(self, key: str, no_prefix: bool = False) -> None:
self.internal_publish(key, None)

def internal_publish(self, key: str, value: Any) -> None:
LOG.debug(f"{key}: {value}")
def internal_publish(self, key: str, value: Any, *, retain: bool = True) -> None:
LOG.debug(f"{key}: {value} (retain={retain})")
32 changes: 24 additions & 8 deletions src/publisher/mqtt_publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,20 +248,36 @@ def publish_json(
)

@override
def publish_str(self, key: str, value: str, no_prefix: bool = False) -> None:
self.__publish(topic=self.get_topic(key, no_prefix), payload=value)
def publish_str(
self, key: str, value: str, no_prefix: bool = False, *, retain: bool = True
) -> None:
self.__publish(
topic=self.get_topic(key, no_prefix), payload=value, retain=retain
)

@override
def publish_int(self, key: str, value: int, no_prefix: bool = False) -> None:
self.__publish(topic=self.get_topic(key, no_prefix), payload=value)
def publish_int(
self, key: str, value: int, no_prefix: bool = False, *, retain: bool = True
) -> None:
self.__publish(
topic=self.get_topic(key, no_prefix), payload=value, retain=retain
)

@override
def publish_bool(self, key: str, value: bool, no_prefix: bool = False) -> None:
self.__publish(topic=self.get_topic(key, no_prefix), payload=value)
def publish_bool(
self, key: str, value: bool, no_prefix: bool = False, *, retain: bool = True
) -> None:
self.__publish(
topic=self.get_topic(key, no_prefix), payload=value, retain=retain
)

@override
def publish_float(self, key: str, value: float, no_prefix: bool = False) -> None:
self.__publish(topic=self.get_topic(key, no_prefix), payload=value)
def publish_float(
self, key: str, value: float, no_prefix: bool = False, *, retain: bool = True
) -> None:
self.__publish(
topic=self.get_topic(key, no_prefix), payload=value, retain=retain
)

@override
def clear_topic(self, key: str, no_prefix: bool = False) -> None:
Expand Down
4 changes: 2 additions & 2 deletions tests/mocks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(self, configuration: Configuration) -> None:
self.publish_count: dict[str, int] = {}

@override
def internal_publish(self, key: str, value: Any) -> None:
def internal_publish(self, key: str, value: Any, *, retain: bool = True) -> None:
self.map[key] = value
self.publish_count[key] = self.publish_count.get(key, 0) + 1
LOG.debug(f"{key}: {value}")
LOG.debug(f"{key}: {value} (retain={retain})")
62 changes: 62 additions & 0 deletions tests/test_mqtt_publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import Any, override
import unittest
from unittest.mock import patch

from configuration import Configuration, TransportProtocol
from publisher.core import MqttCommandListener
Expand Down Expand Up @@ -98,3 +99,64 @@ async def on_charger_connection_state_changed(
self, vin: str, connected: bool
) -> None:
pass

def test_publish_str_default_is_retained(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_str("foo", "bar")
m_pub.assert_called_once_with("saic/foo", "bar", retain=True)

def test_publish_str_forwards_retain_false(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_str("foo", "bar", retain=False)
m_pub.assert_called_once_with("saic/foo", "bar", retain=False)

def test_publish_int_default_is_retained(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_int("foo", 42)
m_pub.assert_called_once_with("saic/foo", 42, retain=True)

def test_publish_int_forwards_retain_false(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_int("foo", 42, retain=False)
m_pub.assert_called_once_with("saic/foo", 42, retain=False)

def test_publish_bool_default_is_retained(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_bool("foo", True)
m_pub.assert_called_once_with("saic/foo", True, retain=True)

def test_publish_bool_forwards_retain_false(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_bool("foo", True, retain=False)
m_pub.assert_called_once_with("saic/foo", True, retain=False)

def test_publish_float_default_is_retained(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_float("foo", 1.5)
m_pub.assert_called_once_with("saic/foo", 1.5, retain=True)

def test_publish_float_forwards_retain_false(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_float("foo", 1.5, retain=False)
m_pub.assert_called_once_with("saic/foo", 1.5, retain=False)

def test_publish_json_default_is_retained(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_json("foo", {"a": 1})
m_pub.assert_called_once()
args, kwargs = m_pub.call_args
assert args[0] == "saic/foo"
assert kwargs == {"retain": True}

def test_publish_json_forwards_retain_false(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.publish_json("foo", {"a": 1}, retain=False)
m_pub.assert_called_once()
args, kwargs = m_pub.call_args
assert args[0] == "saic/foo"
assert kwargs == {"retain": False}

def test_clear_topic_publishes_none_retained(self) -> None:
with patch.object(self.mqtt_client.client, "publish") as m_pub:
self.mqtt_client.clear_topic("foo")
m_pub.assert_called_once_with("saic/foo", None, retain=True)