"""Test midnight-crossing period assignment with group_periods_by_day().""" from __future__ import annotations from datetime import datetime, timedelta from zoneinfo import ZoneInfo import pytest from custom_components.tibber_prices.coordinator.period_handlers.relaxation import ( group_periods_by_day, ) @pytest.fixture def base_date() -> datetime: """Provide base date for tests.""" return datetime(2025, 11, 21, 0, 0, 0, tzinfo=ZoneInfo("Europe/Berlin")) def create_test_period(start_hour: int, end_hour: int, base_date: datetime) -> dict: """Create a test period dict.""" start = base_date.replace(hour=start_hour, minute=0, second=0, microsecond=0) # Handle periods crossing midnight if end_hour < start_hour: end = (base_date + timedelta(days=1)).replace(hour=end_hour, minute=0, second=0, microsecond=0) else: end = base_date.replace(hour=end_hour, minute=0, second=0, microsecond=0) return { "start": start, "end": end, "duration_minutes": int((end - start).total_seconds() / 60), "price_median": 25.5, } @pytest.mark.unit def test_period_within_single_day(base_date: datetime) -> None: """Test period completely within one day (10:00-14:00).""" periods = [create_test_period(10, 14, base_date)] result = group_periods_by_day(periods) assert len(result) == 1, f"Expected 1 day, got {len(result)}" @pytest.mark.unit def test_period_crossing_midnight(base_date: datetime) -> None: """Test period crossing midnight (23:00-02:00).""" periods = [create_test_period(23, 2, base_date)] result = group_periods_by_day(periods) assert len(result) == 2, f"Expected 2 days, got {len(result)}" # Verify the period appears in both days today = base_date.date() tomorrow = (base_date + timedelta(days=1)).date() assert today in result, f"Period should appear in {today}" assert tomorrow in result, f"Period should appear in {tomorrow}" @pytest.mark.unit def test_multiple_periods_with_midnight_crossing(base_date: datetime) -> None: """Test multiple periods, some crossing midnight (8:00-12:00, 14:00-18:00, 22:00-03:00).""" periods = [ create_test_period(8, 12, base_date), # Morning, same day create_test_period(14, 18, base_date), # Afternoon, same day create_test_period(22, 3, base_date), # Night, crosses midnight ] result = group_periods_by_day(periods) today = base_date.date() tomorrow = (base_date + timedelta(days=1)).date() # Check that today has 3 periods (all of them) today_periods = result.get(today, []) assert len(today_periods) == 3, f"Today should have 3 periods, got {len(today_periods)}" # Check that tomorrow has 1 period (the midnight-crossing one) tomorrow_periods = result.get(tomorrow, []) assert len(tomorrow_periods) == 1, f"Tomorrow should have 1 period, got {len(tomorrow_periods)}" @pytest.mark.unit def test_period_spanning_three_days(base_date: datetime) -> None: """Test period spanning 3 days (22:00 day1 - 02:00 day3).""" day1 = base_date day3 = base_date + timedelta(days=2) period = { "start": day1.replace(hour=22, minute=0), "end": day3.replace(hour=2, minute=0), "duration_minutes": int((day3.replace(hour=2) - day1.replace(hour=22)).total_seconds() / 60), "price_median": 25.5, } periods = [period] result = group_periods_by_day(periods) assert len(result) == 3, f"Expected 3 days, got {len(result)}" # Verify the period appears in all 3 days day1_date = day1.date() day2_date = (base_date + timedelta(days=1)).date() day3_date = day3.date() assert day1_date in result, f"Period should appear in {day1_date}" assert day2_date in result, f"Period should appear in {day2_date}" assert day3_date in result, f"Period should appear in {day3_date}" @pytest.mark.unit def test_min_periods_scenario(base_date: datetime) -> None: """Test real-world scenario with min_periods=2 per day.""" # Yesterday: 2 periods (one crosses midnight to today) yesterday = base_date - timedelta(days=1) periods = [ create_test_period(10, 14, yesterday), # Yesterday 10-14 create_test_period(23, 2, yesterday), # Yesterday 23 - Today 02 (crosses midnight!) create_test_period(15, 19, base_date), # Today 15-19 ] result = group_periods_by_day(periods) yesterday_date = yesterday.date() today_date = base_date.date() yesterday_periods = result.get(yesterday_date, []) today_periods = result.get(today_date, []) # Both days should have 2 periods (min_periods requirement met) assert len(yesterday_periods) == 2, "Yesterday should have 2 periods" assert len(today_periods) == 2, "Today should have 2 periods (including midnight-crosser)"