mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 13:23:41 +00:00
131 lines
5.8 KiB
Python
131 lines
5.8 KiB
Python
"""Integration test for price utils with realistic data."""
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from custom_components.tibber_prices.price_utils import enrich_price_info_with_differences
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
|
|
def generate_price_intervals(base_time: datetime, hours: int, base_price: float, variation: float = 0.05) -> list:
|
|
"""Generate realistic price intervals."""
|
|
intervals = []
|
|
for i in range(hours * 4): # 4 intervals per hour (15-minute intervals)
|
|
time = base_time + timedelta(minutes=15 * i)
|
|
# Add sinusoidal variation (peak at 18:00, low at 6:00)
|
|
hour_of_day = time.hour + time.minute / 60
|
|
variation_factor = 1 + variation * (((hour_of_day - 6) / 12) * 3.14159)
|
|
price = base_price * (1 + 0.1 * (variation_factor - 1))
|
|
|
|
intervals.append(
|
|
{
|
|
"startsAt": time.isoformat(),
|
|
"total": price,
|
|
"energy": price * 0.75,
|
|
"tax": price * 0.25,
|
|
"level": "NORMAL",
|
|
}
|
|
)
|
|
|
|
return intervals
|
|
|
|
|
|
def test_realistic_day_pricing() -> None:
|
|
"""Test with realistic pricing patterns across 48 hours."""
|
|
base_time = dt_util.now().replace(hour=12, minute=0, second=0, microsecond=0)
|
|
|
|
# Generate realistic data
|
|
price_info = {
|
|
"yesterday": generate_price_intervals(base_time - timedelta(days=1), hours=24, base_price=0.12, variation=0.08),
|
|
"today": generate_price_intervals(
|
|
base_time.replace(hour=0, minute=0), hours=24, base_price=0.15, variation=0.10
|
|
),
|
|
"tomorrow": generate_price_intervals(
|
|
base_time.replace(hour=0, minute=0) + timedelta(days=1), hours=24, base_price=0.13, variation=0.07
|
|
),
|
|
}
|
|
|
|
# Enrich with differences
|
|
enriched = enrich_price_info_with_differences(price_info)
|
|
|
|
# Verify all today intervals have differences
|
|
today_intervals = enriched["today"]
|
|
for interval in today_intervals:
|
|
assert "difference" in interval, f"Missing difference in today interval {interval['startsAt']}"
|
|
assert "rating_level" in interval, f"Missing rating_level in today interval {interval['startsAt']}"
|
|
|
|
# Verify all tomorrow intervals have differences
|
|
tomorrow_intervals = enriched["tomorrow"]
|
|
for interval in tomorrow_intervals:
|
|
assert "difference" in interval, f"Missing difference in tomorrow interval {interval['startsAt']}"
|
|
assert "rating_level" in interval, f"Missing rating_level in tomorrow interval {interval['startsAt']}"
|
|
|
|
# Verify yesterday is unchanged (except for missing difference)
|
|
yesterday_intervals = enriched["yesterday"]
|
|
assert len(yesterday_intervals) == 96
|
|
|
|
# Analyze statistics
|
|
today_diffs = [i.get("difference") for i in today_intervals if i.get("difference") is not None]
|
|
today_levels = [i.get("rating_level") for i in today_intervals if i.get("rating_level") is not None]
|
|
tomorrow_levels = [i.get("rating_level") for i in tomorrow_intervals if i.get("rating_level") is not None]
|
|
|
|
# Verify rating_level values are valid
|
|
valid_levels = {"LOW", "NORMAL", "HIGH"}
|
|
assert all(level in valid_levels for level in today_levels), "Invalid rating_level in today intervals"
|
|
assert all(level in valid_levels for level in tomorrow_levels), "Invalid rating_level in tomorrow intervals"
|
|
|
|
# With realistic pricing variation and default thresholds of -10/+10,
|
|
# we should have at least 2 different levels (most likely HIGH and NORMAL for today,
|
|
# and NORMAL for tomorrow due to cheaper prices)
|
|
unique_today_levels = set(today_levels)
|
|
assert len(unique_today_levels) >= 1, "Today should have at least one rating level"
|
|
|
|
|
|
def test_day_boundary_calculations() -> None:
|
|
"""Test calculations across midnight boundary."""
|
|
midnight = dt_util.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
|
|
# Create data that spans the midnight boundary
|
|
price_info = {
|
|
"yesterday": generate_price_intervals(midnight - timedelta(days=1), hours=24, base_price=0.10),
|
|
"today": generate_price_intervals(midnight, hours=24, base_price=0.15),
|
|
"tomorrow": generate_price_intervals(midnight + timedelta(days=1), hours=24, base_price=0.12),
|
|
}
|
|
|
|
enriched = enrich_price_info_with_differences(price_info)
|
|
|
|
# Check the midnight boundary interval (first of tomorrow)
|
|
midnight_tomorrow = enriched["tomorrow"][0]
|
|
|
|
# This should include all 96 intervals from yesterday and all 96 from today
|
|
assert "difference" in midnight_tomorrow
|
|
diff = midnight_tomorrow.get("difference")
|
|
|
|
# Since tomorrow is cheaper (0.12) than both yesterday (0.10) and today (0.15)
|
|
# The difference could be negative (cheap) or positive (expensive) depending on the mix
|
|
diff = midnight_tomorrow.get("difference")
|
|
assert diff is not None, "Midnight boundary interval should have difference"
|
|
|
|
|
|
def test_early_morning_calculations() -> None:
|
|
"""Test calculations in early morning hours."""
|
|
base_time = dt_util.now().replace(hour=6, minute=0, second=0, microsecond=0)
|
|
|
|
price_info = {
|
|
"yesterday": generate_price_intervals(base_time - timedelta(days=1), hours=24, base_price=0.12),
|
|
"today": generate_price_intervals(base_time.replace(hour=0, minute=0), hours=24, base_price=0.15),
|
|
"tomorrow": generate_price_intervals(
|
|
base_time.replace(hour=0, minute=0) + timedelta(days=1), hours=24, base_price=0.13
|
|
),
|
|
}
|
|
|
|
enriched = enrich_price_info_with_differences(price_info)
|
|
|
|
# Get 6 AM interval (24th interval of the day)
|
|
six_am_interval = enriched["today"][24]
|
|
assert "difference" in six_am_interval
|
|
|
|
# At 6 AM, we should include:
|
|
# - Yesterday from 6 AM to midnight (68 intervals)
|
|
# - Today from midnight to 6 AM (24 intervals)
|
|
# Total: 92 intervals (not quite 24 hours)
|
|
assert "difference" in six_am_interval
|