Skip to content

Commit 227f573

Browse files
test(web): address Copilot review feedback on the Rates chart currency test
Two issues flagged on PR #4288: - The test mutated my_predbat.currency_symbols without restoring it, leaking state into later tests in the same suite process (the shared my_predbat fixture is reused across all tests). - The test never actually called WebInterface.get_chart()/web.py - it only re-tested the same inline string-formatting logic, so it would still pass even if web.py regressed back to a hardcoded "p/kWh". Now builds a minimal WebInterface (bypassing ComponentBase.__init__, which would stand up the real aiohttp app) and calls the real get_chart("Rates"), stubbing get_history_wrapper() directly to avoid reproducing realistic HA history formatting for data not under test. Verified this actually catches a regression: temporarily reintroducing the hardcoded "p/kWh" strings in web.py makes the test fail as expected. currency_symbols/dashboard_values are now saved and restored via try/finally. 🤖 Implemented with Claude Code, disclosed per repo convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent cfc83c0 commit 227f573

1 file changed

Lines changed: 66 additions & 15 deletions

File tree

‎apps/predbat/tests/test_web_chart_currency.py‎

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,77 @@
1414
user configured for NZ dollars/cents saw "p/kWh" (pence) in the chart legend regardless.
1515
"""
1616

17+
import re
18+
19+
from web import WebInterface
20+
21+
22+
def _make_web(my_predbat):
23+
"""
24+
Build a minimal WebInterface bound to my_predbat, bypassing ComponentBase.__init__ (which
25+
would stand up the real aiohttp app). currency_symbols/now_utc/minutes_now/etc are all
26+
read-only properties on ComponentBase that delegate to self.base, so nothing else needs
27+
setting here for get_chart() to run.
28+
"""
29+
w = WebInterface.__new__(WebInterface)
30+
w.base = my_predbat
31+
w.log = my_predbat.log
32+
w.prefix = my_predbat.prefix
33+
return w
34+
1735

1836
def test_rates_chart_series_names_use_currency_symbol(my_predbat):
1937
"""
20-
Directly mirrors the series-name formatting used in WebInterface.get_chart's "Rates"
21-
branch (web.py) - get_chart itself requires a fully computed plan (soc_kw_best populated)
22-
before it reaches this branch, so this tests the formatting logic directly rather than
23-
standing up that heavier machinery.
38+
Calls the real WebInterface.get_chart("Rates") and checks the rendered series names,
39+
rather than re-testing the formatting logic in isolation - a prior version of this test
40+
only duplicated the format string inline, so it would still pass even if web.py regressed
41+
back to a hardcoded "p/kWh" (Copilot review on PR #4288).
42+
43+
get_chart() gates on soc_kw_best being populated (dashboard_values) and reads the Hourly/
44+
Today series from get_history_wrapper() - both are stubbed directly rather than trying to
45+
reproduce realistic HA history formatting, since only the currency-dependent series *names*
46+
are under test here, not the underlying rate data.
2447
"""
2548
print("**** test_rates_chart_series_names_use_currency_symbol ****")
2649

27-
for currency_symbols, expected_minor in [("£p", "p"), ("$c", "c"), ("€c", "c")]:
28-
my_predbat.currency_symbols = currency_symbols
29-
hourly_name = "Hourly {}/kWh".format(my_predbat.currency_symbols[1])
30-
today_name = "Today {}/kWh".format(my_predbat.currency_symbols[1])
50+
original_currency_symbols = my_predbat.currency_symbols
51+
original_dashboard_values = getattr(my_predbat, "dashboard_values", None)
52+
53+
try:
54+
w = _make_web(my_predbat)
55+
my_predbat.dashboard_values = {
56+
"predbat.soc_kw_best": {"attributes": {"results": {"2026-01-01T00:00:00+00:00": 5.0}}},
57+
"predbat.rates": {"attributes": {"results": {"2026-01-01T00:00:00+00:00": 10.0}}},
58+
}
59+
fake_history = [
60+
[
61+
{"state": 10.5, "last_updated": "2026-01-01T00:00:00+00:00"},
62+
{"state": 12.3, "last_updated": "2026-01-01T00:30:00+00:00"},
63+
]
64+
]
65+
w.get_history_wrapper = lambda *a, **kw: fake_history
66+
67+
for currency_symbols, expected_minor in [("£p", "p"), ("$c", "c"), ("€c", "c")]:
68+
my_predbat.currency_symbols = currency_symbols
69+
70+
result = w.get_chart("Rates")
71+
series_names = re.findall(r"name: '([^']*)'", result)
72+
73+
expected_hourly = "Hourly {}/kWh".format(expected_minor)
74+
expected_today = "Today {}/kWh".format(expected_minor)
3175

32-
assert hourly_name == "Hourly {}/kWh".format(expected_minor), f"Expected 'Hourly {expected_minor}/kWh', got '{hourly_name}'"
33-
assert today_name == "Today {}/kWh".format(expected_minor), f"Expected 'Today {expected_minor}/kWh', got '{today_name}'"
34-
if expected_minor != "p":
35-
assert "p/kWh" not in hourly_name and "p/kWh" not in today_name, f"Series name should not be hardcoded to pence for currency_symbols={currency_symbols}"
76+
assert expected_hourly in series_names, f"Expected series '{expected_hourly}' in {series_names} for currency_symbols={currency_symbols}"
77+
assert expected_today in series_names, f"Expected series '{expected_today}' in {series_names} for currency_symbols={currency_symbols}"
78+
if expected_minor != "p":
79+
assert not any("p/kWh" in name for name in series_names), f"Series names should not be hardcoded to pence for currency_symbols={currency_symbols}, got {series_names}"
3680

37-
print("✓ Rates chart series names correctly follow currency_symbols[1] (£p, $c, €c all verified)")
38-
print("✓ Test passed")
39-
return False
81+
print("✓ Rates chart series names correctly follow currency_symbols[1] (£p, $c, €c all verified against the real get_chart() output)")
82+
print("✓ Test passed")
83+
return False
84+
finally:
85+
my_predbat.currency_symbols = original_currency_symbols
86+
if original_dashboard_values is None:
87+
if hasattr(my_predbat, "dashboard_values"):
88+
del my_predbat.dashboard_values
89+
else:
90+
my_predbat.dashboard_values = original_dashboard_values

0 commit comments

Comments
 (0)