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
2 changes: 1 addition & 1 deletion src/enapter/cli/http/api/site_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
def parse_site_location(location_str: str) -> tuple[str, float, float]:
try:
name, lat_str, lon_str = location_str.split(",")
return name, float(lat_str), float(lon_str)
return name.strip(), float(lat_str), float(lon_str)
except ValueError:
raise argparse.ArgumentTypeError(
"Location must be in the format NAME,LATITUDE,LONGITUDE"
Expand Down
58 changes: 58 additions & 0 deletions tests/unit/test_cli/test_http/test_api/test_site_location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import argparse

import pytest

from enapter.cli.http.api.site_location import parse_site_location


def test_parse_site_location_valid():
assert parse_site_location("Berlin,52.52,13.405") == ("Berlin", 52.52, 13.405)


def test_parse_site_location_with_spaces():
# Note: name strips whitespace, float() handles surrounding whitespace
assert parse_site_location(" Berlin , 52.52 , 13.405 ") == (
Comment thread
rnovatorov marked this conversation as resolved.
"Berlin",
52.52,
13.405,
)


def test_parse_site_location_too_few_parts():
with pytest.raises(argparse.ArgumentTypeError) as exc_info:
parse_site_location("Berlin,52.52")
assert "Location must be in the format NAME,LATITUDE,LONGITUDE" in str(
exc_info.value
)


def test_parse_site_location_too_many_parts():
with pytest.raises(argparse.ArgumentTypeError) as exc_info:
parse_site_location("Berlin,52.52,13.405,extra")
assert "Location must be in the format NAME,LATITUDE,LONGITUDE" in str(
exc_info.value
)


def test_parse_site_location_invalid_latitude():
with pytest.raises(argparse.ArgumentTypeError) as exc_info:
parse_site_location("Berlin,invalid,13.405")
assert "Location must be in the format NAME,LATITUDE,LONGITUDE" in str(
exc_info.value
)


def test_parse_site_location_invalid_longitude():
with pytest.raises(argparse.ArgumentTypeError) as exc_info:
parse_site_location("Berlin,52.52,invalid")
assert "Location must be in the format NAME,LATITUDE,LONGITUDE" in str(
exc_info.value
)


def test_parse_site_location_empty():
with pytest.raises(argparse.ArgumentTypeError) as exc_info:
parse_site_location("")
assert "Location must be in the format NAME,LATITUDE,LONGITUDE" in str(
exc_info.value
)
42 changes: 33 additions & 9 deletions tests/unit/test_standalone/test_mqtt_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ async def test_publish_properties():
device = Device()
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
event = asyncio.Event()
device_channel.publish_properties.side_effect = lambda *args, **kwargs: event.set()
mqtt_api_client.device_channel.return_value = device_channel
async with asyncio.TaskGroup() as tg:
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
Expand All @@ -59,7 +61,7 @@ async def test_publish_properties():
device=device,
task_group=tg,
):
await asyncio.sleep(0.02)
await asyncio.wait_for(event.wait(), timeout=1.0)
device_channel.publish_properties.assert_called()
last_call = device_channel.publish_properties.call_args
published_properties = last_call.kwargs["properties"]
Expand All @@ -71,6 +73,8 @@ async def test_publish_telemetry():
device = Device()
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
event = asyncio.Event()
device_channel.publish_telemetry.side_effect = lambda *args, **kwargs: event.set()
mqtt_api_client.device_channel.return_value = device_channel
async with asyncio.TaskGroup() as tg:
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
Expand All @@ -80,7 +84,7 @@ async def test_publish_telemetry():
device=device,
task_group=tg,
):
await asyncio.sleep(0.02)
await asyncio.wait_for(event.wait(), timeout=1.0)
device_channel.publish_telemetry.assert_called()
last_call = device_channel.publish_telemetry.call_args
published_telemetry = last_call.kwargs["telemetry"]
Expand All @@ -94,6 +98,8 @@ async def test_publish_logs(log_severity, persist_logs) -> None:
device = Device(log_severity=log_severity, persist_logs=persist_logs)
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
event = asyncio.Event()
device_channel.publish_log.side_effect = lambda *args, **kwargs: event.set()
mqtt_api_client.device_channel.return_value = device_channel
async with asyncio.TaskGroup() as tg:
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
Expand All @@ -103,7 +109,7 @@ async def test_publish_logs(log_severity, persist_logs) -> None:
device=device,
task_group=tg,
):
await asyncio.sleep(0.02)
await asyncio.wait_for(event.wait(), timeout=1.0)
device_channel.publish_log.assert_called()
last_call = device_channel.publish_log.call_args
published_log = last_call.kwargs["log"]
Expand All @@ -119,7 +125,13 @@ async def test_publish_properties_exception():
device = Device()
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
device_channel.publish_properties.side_effect = RuntimeError("Publish error")
event = asyncio.Event()

def publish_properties_mock(*args, **kwargs):
event.set()
raise RuntimeError("Publish error")

device_channel.publish_properties.side_effect = publish_properties_mock
mqtt_api_client.device_channel.return_value = device_channel
async with asyncio.TaskGroup() as tg:
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
Expand All @@ -129,15 +141,21 @@ async def test_publish_properties_exception():
device=device,
task_group=tg,
):
await asyncio.sleep(0.02)
await asyncio.wait_for(event.wait(), timeout=1.0)
device_channel.publish_properties.assert_called()


async def test_publish_telemetry_exception():
device = Device()
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
device_channel.publish_telemetry.side_effect = RuntimeError("Publish error")
event = asyncio.Event()

def publish_telemetry_mock(*args, **kwargs):
event.set()
raise RuntimeError("Publish error")

device_channel.publish_telemetry.side_effect = publish_telemetry_mock
mqtt_api_client.device_channel.return_value = device_channel
async with asyncio.TaskGroup() as tg:
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
Expand All @@ -147,15 +165,21 @@ async def test_publish_telemetry_exception():
device=device,
task_group=tg,
):
await asyncio.sleep(0.02)
await asyncio.wait_for(event.wait(), timeout=1.0)
device_channel.publish_telemetry.assert_called()


async def test_publish_logs_exception():
device = Device(log_severity="error")
mqtt_api_client = mock.AsyncMock(spec=enapter.mqtt.api.Client)
device_channel = mock.AsyncMock(spec=enapter.mqtt.api.device.Channel)
device_channel.publish_log.side_effect = RuntimeError("Publish error")
event = asyncio.Event()

def publish_log_mock(*args, **kwargs):
event.set()
raise RuntimeError("Publish error")

device_channel.publish_log.side_effect = publish_log_mock
mqtt_api_client.device_channel.return_value = device_channel
async with asyncio.TaskGroup() as tg:
async with enapter.standalone.mqtt_adapter.MQTTAdapter(
Expand All @@ -165,7 +189,7 @@ async def test_publish_logs_exception():
device=device,
task_group=tg,
):
await asyncio.sleep(0.02)
await asyncio.wait_for(event.wait(), timeout=1.0)
device_channel.publish_log.assert_called()


Expand Down
Loading