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
17 changes: 16 additions & 1 deletion packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@

class UpdateConfig:

DATASTORE_VERSION = 155
DATASTORE_VERSION = 156

FILE_OPERATION_VERSION = 0

Expand Down Expand Up @@ -4191,3 +4191,18 @@ def upgrade(topic: str, payload) -> Optional[dict]:

self._loop_all_received_topics(upgrade)
self._append_datastore_version(155)

def upgrade_datastore_156(self) -> None:
"""Optionale Preisobergrenze für Energy-Charts ergänzen."""
def upgrade(topic: str, payload) -> Optional[dict]:
if topic == "openWB/optional/ep/flexible_tariff/provider":
provider = decode_payload(payload)
if provider.get("type") == "energycharts":
config = provider["configuration"]
if "price_cap" not in config:
config["price_cap"] = None
return {topic: provider}
return None

self._loop_all_received_topics(upgrade)
self._append_datastore_version(156)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Failed migration is marked complete

When the retained provider payload has not arrived, decoding fails, or an Energy-Charts payload has a missing or null configuration, _loop_all_received_topics leaves the provider unmigrated, but upgrade_datastore_156 still calls _append_datastore_version(156), so later startups skip the migration and the configuration stays incomplete.

Only append datastore version 156 after confirming the provider migration completed successfully.

Also at packages/helpermodules/update_config.py:4201, packages/helpermodules/update_config.py:4207.

Prompt for AI agents
In `packages/helpermodules/update_config.py` at line 4208:

**Failed migration is marked complete**

When the retained provider payload has not arrived, decoding fails, or an Energy-Charts payload has a missing or null configuration, `_loop_all_received_topics` leaves the provider unmigrated, but `upgrade_datastore_156` still calls `_append_datastore_version(156)`, so later startups skip the migration and the configuration stays incomplete.

Only append datastore version 156 after confirming the provider migration completed successfully.

Also at `packages/helpermodules/update_config.py:4201`, `packages/helpermodules/update_config.py:4207`.

32 changes: 32 additions & 0 deletions packages/helpermodules/update_config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,3 +634,35 @@ def test_file_operation_0_start_behavior(file_operation_version, finished, expec
with patch.object(update_config, "upgrade_file_operation_0") as upgrade_mock:
update_config._UpdateConfig__solve_breaking_changes_filesystem()
assert upgrade_mock.call_count == expected_calls


@pytest.mark.parametrize("provider_type, configuration, expected_configuration, updated", [
("energycharts", {"surcharge": 0.0001}, {"surcharge": 0.0001, "price_cap": None}, True),
("energycharts", {"price_cap": 0.0002}, {"price_cap": 0.0002}, False),
("energycharts", {"price_cap": None}, {"price_cap": None}, False),
("tibber", {}, {}, False),
(None, {}, {}, False),
])
def test_upgrade_datastore_156_adds_energycharts_price_cap(
mock_pub: Mock, provider_type, configuration, expected_configuration, updated
):
topic = "openWB/optional/ep/flexible_tariff/provider"
uc = UpdateConfig()
uc.all_received_topics = {
topic: json.dumps({"type": provider_type, "configuration": configuration}).encode(),
"openWB/system/datastore_version": [155],
}

uc.upgrade_datastore_156()

if updated:
assert uc.all_received_topics[topic] == {
"type": provider_type, "configuration": expected_configuration
}
else:
assert json.loads(uc.all_received_topics[topic]) == {
"type": provider_type, "configuration": expected_configuration
}
updated_topics = [call.args[0] for call in mock_pub.pub.call_args_list]
assert (topic in updated_topics) is updated
Comment on lines +666 to +667

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Wrong migration payload goes unnoticed

When Pub.pub receives a payload different from the migrated provider configuration, test_upgrade_datastore_156_adds_energycharts_price_cap extracts only call.args[0] at line 666 and checks only the topic at line 667, so it passes when Pub.pub publishes an incorrect payload for that topic. The migration can therefore publish the wrong provider configuration without this test detecting it.

Assert that the publication for the provider topic carries the expected migrated configuration.

Prompt for AI agents
In `packages/helpermodules/update_config_test.py` at lines 666-667:

**Wrong migration payload goes unnoticed**

When `Pub.pub` receives a payload different from the migrated provider configuration, `test_upgrade_datastore_156_adds_energycharts_price_cap` extracts only `call.args[0]` at line 666 and checks only the topic at line 667, so it passes when `Pub.pub` publishes an incorrect payload for that topic. The migration can therefore publish the wrong provider configuration without this test detecting it.

Assert that the publication for the provider topic carries the expected migrated configuration.

assert uc.all_received_topics["openWB/system/datastore_version"] == [155, 156]
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass, field
from typing import Optional


@dataclass
Expand All @@ -7,6 +8,7 @@ class EnergyChartsTariffConfiguration:
surcharge: float = 0
net: bool = True
tax: float = 19
price_cap: Optional[float] = None


@dataclass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ def create_request_url(config: EnergyChartsTariffConfiguration) -> str:
def parse_response(config: EnergyChartsTariffConfiguration, raw_prices: Dict[str, float]) -> Dict[str, float]:
prices: Dict[str, float] = {}
for timestamp, price_per_MWh in zip(raw_prices['unix_seconds'], raw_prices['price']):
formatted_price = float(price_per_MWh)/1000000 + config.surcharge
formatted_price = float(price_per_MWh)/1000000
if config.price_cap is not None:
formatted_price = min(formatted_price, config.price_cap)
formatted_price += config.surcharge
if config.net is False:
formatted_price = formatted_price * (1 + config.tax / 100)
prices[str(int(timestamp))] = formatted_price
Expand Down