M8-T05: bind electricity costs to source bindings

This commit is contained in:
2026-08-23 21:22:05 +02:00
parent 2e125dbd53
commit 1ea2f659e0
10 changed files with 1057 additions and 304 deletions
+35 -5
View File
@@ -1097,6 +1097,9 @@ def test_import_cost_total_includes_standing_charges(energy_db) -> None:
from app.services import timezone as _tz_mod
now_utc = datetime.now(timezone.utc)
# Keep the dynamic test date, but settle it at a deterministic business time
# beyond the local 01:05 fixed-fee/credit cutoff.
settled_local_now = now_utc.replace(hour=12, minute=0, second=0, microsecond=0)
# D2 anchor = meter.started_at = 10 UTC days ago at midnight
meter_started_at = now_utc.replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=10)
effective_from = meter_started_at # contract also starts at the same time
@@ -1123,7 +1126,10 @@ def test_import_cost_total_includes_standing_charges(energy_db) -> None:
with Session(energy_db) as session:
# Pin to UTC so local days = UTC days (deterministic on any CI host).
with patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")):
with (
patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")),
patch("app.services.energy_cost.local_now", return_value=settled_local_now),
):
catalog = build_catalog(session)
import_entry = next(
e for e in catalog if e.entity.key == "energy.import_cost_total"
@@ -1163,6 +1169,9 @@ def test_export_revenue_total_includes_tax_credit(energy_db) -> None:
from app.services import timezone as _tz_mod
now_utc = datetime.now(timezone.utc)
# Keep the dynamic test date, but settle it at a deterministic business time
# beyond the local 01:05 fixed-fee/credit cutoff.
settled_local_now = now_utc.replace(hour=12, minute=0, second=0, microsecond=0)
meter_started_at = now_utc.replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=4)
effective_from = meter_started_at
@@ -1186,7 +1195,10 @@ def test_export_revenue_total_includes_tax_credit(energy_db) -> None:
session.commit()
with Session(energy_db) as session:
with patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")):
with (
patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")),
patch("app.services.energy_cost.local_now", return_value=settled_local_now),
):
catalog = build_catalog(session)
export_entry = next(
e for e in catalog if e.entity.key == "energy.export_revenue_total"
@@ -1945,6 +1957,9 @@ def test_cumulative_anchor_is_meter_started_at(energy_db) -> None:
from app.services import timezone as _tz_mod
now_utc = datetime.now(timezone.utc)
# Keep the dynamic test date, but settle it at a deterministic business time
# beyond the local 01:05 fixed-fee/credit cutoff.
settled_local_now = now_utc.replace(hour=12, minute=0, second=0, microsecond=0)
midnight_today = now_utc.replace(hour=0, minute=0, second=0, microsecond=0)
# Contract starts 180 days ago (far before the meter)
@@ -1966,7 +1981,10 @@ def test_cumulative_anchor_is_meter_started_at(energy_db) -> None:
session.commit()
with Session(energy_db) as session:
with patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")):
with (
patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")),
patch("app.services.energy_cost.local_now", return_value=settled_local_now),
):
catalog = build_catalog(session)
import_entry = next(
e for e in catalog if e.entity.key == "energy.import_cost_total"
@@ -2089,6 +2107,9 @@ def test_cumulative_resets_after_meter_swap(energy_db) -> None:
from app.services import timezone as _tz_mod
now_utc = datetime.now(timezone.utc)
# Keep the dynamic test date, but settle it at a deterministic business time
# beyond the local 01:05 fixed-fee/credit cutoff.
settled_local_now = now_utc.replace(hour=12, minute=0, second=0, microsecond=0)
midnight_today = now_utc.replace(hour=0, minute=0, second=0, microsecond=0)
old_meter_start = midnight_today - timedelta(days=30)
@@ -2145,7 +2166,10 @@ def test_cumulative_resets_after_meter_swap(energy_db) -> None:
session.commit()
with Session(energy_db) as session:
with patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")):
with (
patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")),
patch("app.services.energy_cost.local_now", return_value=settled_local_now),
):
catalog = build_catalog(session)
import_entry = next(
e for e in catalog if e.entity.key == "energy.import_cost_total"
@@ -2194,6 +2218,9 @@ def test_daily_getters_unaffected_by_d2_meter_anchor(energy_db) -> None:
from app.services import timezone as _tz_mod
now_utc = datetime.now(timezone.utc)
# Keep the dynamic test date, but settle it at a deterministic business time
# beyond the local 01:05 fixed-fee/credit cutoff.
settled_local_now = now_utc.replace(hour=12, minute=0, second=0, microsecond=0)
# Period 1h ago — in today's UTC window
t0 = now_utc.replace(minute=0, second=0, microsecond=0) - timedelta(hours=1)
if t0.date() < now_utc.date():
@@ -2226,7 +2253,10 @@ def test_daily_getters_unaffected_by_d2_meter_anchor(energy_db) -> None:
session.commit()
with Session(energy_db) as session:
with patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")):
with (
patch.object(_tz_mod, "local_tz", return_value=ZoneInfo("UTC")),
patch("app.services.energy_cost.local_now", return_value=settled_local_now),
):
catalog = build_catalog(session)
import_today_entry = next(
e for e in catalog if e.entity.key == "energy.import_cost_today"