Files
EOS/tests/test_adapternodered.py
Bobby Noelte c914bae667 fix: data management conversion to async (#1185)
Fix test warnings left over from data management conversion to async design.
Mostly tagging async tests.

Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
2026-07-22 14:50:55 +02:00

134 lines
5.0 KiB
Python

from __future__ import annotations
from datetime import datetime
from unittest.mock import MagicMock, patch
import pytest
from pydantic import BaseModel
from akkudoktoreos.adapter.adapter import AdapterCommonSettings
from akkudoktoreos.adapter.nodered import NodeREDAdapter, NodeREDAdapterCommonSettings
from akkudoktoreos.core.emplan import DDBCInstruction, FRBCInstruction
from akkudoktoreos.core.ems import EnergyManagementStage
from akkudoktoreos.utils.datetimeutil import DateTime, compare_datetimes, to_datetime
@pytest.fixture
def mock_ems() -> MagicMock:
m = MagicMock()
m.stage.return_value = EnergyManagementStage.DATA_ACQUISITION
m.plan.return_value.get_active_instructions.return_value = []
return m
@pytest.fixture
def adapter(config_eos, mock_ems: MagicMock) -> NodeREDAdapter:
"""Fully Pydantic-safe NodeREDAdapter fixture."""
# Set nested value - also fills None values
config_eos.set_nested_value("adapter/provider", ["NodeRED"])
ad = NodeREDAdapter()
# Mark update datetime invalid
ad.update_datetime = None
# Assign EMS
object.__setattr__(ad, "ems", mock_ems)
return ad
class TestNodeREDAdapter:
def test_provider_id(self, adapter: NodeREDAdapter):
assert adapter.provider_id() == "NodeRED"
def test_enabled_detection_single(self, adapter: NodeREDAdapter):
adapter.config.adapter.provider = ["NodeRED"]
assert adapter.enabled() is True
adapter.config.adapter.provider = ["HomeAssistant"]
assert adapter.enabled() is False
adapter.config.adapter.provider = ["HomeAssistant", "NodeRED"]
assert adapter.enabled() is True
@pytest.mark.asyncio
@patch("requests.get")
async def test_update_datetime(self, mock_get, adapter: NodeREDAdapter):
adapter.ems.stage.return_value = EnergyManagementStage.DATA_ACQUISITION
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {"foo": "bar"}
now = to_datetime()
await adapter.update_data(force_enable=True)
mock_get.assert_called_once()
assert compare_datetimes(adapter.update_datetime, now).approximately_equal
@pytest.mark.asyncio
@patch("requests.get")
async def test_update_data_data_acquisition_success(self, mock_get , adapter: NodeREDAdapter):
adapter.ems.stage.return_value = EnergyManagementStage.DATA_ACQUISITION
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {"foo": "bar"}
await adapter.update_data(force_enable=True)
mock_get.assert_called_once()
url, = mock_get.call_args[0]
assert "/eos/data_aquisition" in url
@pytest.mark.asyncio
@patch("requests.get", side_effect=Exception("boom"))
async def test_update_data_data_acquisition_failure(self, mock_get, adapter: NodeREDAdapter):
adapter.ems.stage.return_value = EnergyManagementStage.DATA_ACQUISITION
with pytest.raises(RuntimeError):
await adapter.update_data(force_enable=True)
@pytest.mark.asyncio
@patch("requests.post")
async def test_update_data_control_dispatch_instructions(self, mock_post, adapter: NodeREDAdapter):
adapter.ems.stage.return_value = EnergyManagementStage.CONTROL_DISPATCH
instr1 = DDBCInstruction(
id="res1@extra", operation_mode_id="X", operation_mode_factor=0.5,
actuator_id="dummy", execution_time=to_datetime()
)
instr2 = FRBCInstruction(
id="resA", operation_mode_id="Y", operation_mode_factor=0.25,
actuator_id="dummy", execution_time=to_datetime()
)
adapter.ems.plan.return_value.get_active_instructions.return_value = [instr1, instr2]
mock_post.return_value.status_code = 200
mock_post.return_value.json.return_value = {}
await adapter.update_data(force_enable=True)
_, kwargs = mock_post.call_args
payload = kwargs["json"]
assert payload["res1_op_mode"] == "X"
assert payload["res1_op_factor"] == 0.5
assert payload["resA_op_mode"] == "Y"
assert payload["resA_op_factor"] == 0.25
url, = mock_post.call_args[0]
assert "/eos/control_dispatch" in url
@pytest.mark.asyncio
@patch("requests.post")
async def test_update_data_disabled_provider(self, mock_post, adapter: NodeREDAdapter):
adapter.config.adapter.provider = ["HomeAssistant"] # NodeRED disabled
await adapter.update_data(force_enable=False)
mock_post.assert_not_called()
@pytest.mark.asyncio
@patch("requests.post")
async def test_update_data_force_enable_overrides_disabled(self, mock_post, adapter: NodeREDAdapter):
adapter.config.adapter.provider = ["HomeAssistant"]
adapter.ems.stage.return_value = EnergyManagementStage.CONTROL_DISPATCH
mock_post.return_value.status_code = 200
mock_post.return_value.json.return_value = {}
await adapter.update_data(force_enable=True)
mock_post.assert_called_once()