9e1615bd32
Also adds get_cached_weather_rows() for parallel gather in briefing pipeline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
106 lines
4.0 KiB
Python
106 lines
4.0 KiB
Python
import pytest
|
|
from unittest.mock import AsyncMock, patch, MagicMock
|
|
|
|
|
|
def test_parse_open_meteo_response():
|
|
"""parse_forecast() should extract daily data into a clean list."""
|
|
from fabledassistant.services.weather import parse_forecast
|
|
raw = {
|
|
"daily": {
|
|
"time": ["2026-03-11", "2026-03-12"],
|
|
"temperature_2m_max": [15.0, 12.0],
|
|
"temperature_2m_min": [8.0, 6.0],
|
|
"precipitation_sum": [0.0, 3.2],
|
|
"weathercode": [0, 61],
|
|
"windspeed_10m_max": [10.0, 25.0],
|
|
}
|
|
}
|
|
days = parse_forecast(raw)
|
|
assert len(days) == 2
|
|
assert days[0]["date"] == "2026-03-11"
|
|
assert days[0]["temp_max"] == 15.0
|
|
assert days[0]["temp_min"] == 8.0
|
|
assert days[0]["precip_mm"] == 0.0
|
|
assert days[0]["weathercode"] == 0
|
|
assert days[1]["date"] == "2026-03-12"
|
|
assert days[1]["precip_mm"] == 3.2
|
|
|
|
|
|
def test_describe_weathercode():
|
|
"""describe_weathercode() should return human-readable strings."""
|
|
from fabledassistant.services.weather import describe_weathercode
|
|
assert describe_weathercode(0) == "Clear sky"
|
|
assert describe_weathercode(61) == "Rain: slight"
|
|
assert describe_weathercode(95) == "Thunderstorm"
|
|
assert "Unknown" in describe_weathercode(999)
|
|
|
|
|
|
def test_detect_forecast_changes_no_change():
|
|
"""detect_changes() should return empty list when forecasts are identical."""
|
|
from fabledassistant.services.weather import detect_changes
|
|
day = {"date": "2026-03-12", "temp_max": 12.0, "temp_min": 6.0,
|
|
"precip_mm": 3.2, "weathercode": 61}
|
|
assert detect_changes([day], [day]) == []
|
|
|
|
|
|
def test_detect_forecast_changes_rain_added():
|
|
"""detect_changes() should flag when precipitation appears."""
|
|
from fabledassistant.services.weather import detect_changes
|
|
old = [{"date": "2026-03-12", "temp_max": 14.0, "temp_min": 8.0,
|
|
"precip_mm": 0.0, "weathercode": 2}]
|
|
new = [{"date": "2026-03-12", "temp_max": 12.0, "temp_min": 7.0,
|
|
"precip_mm": 4.5, "weathercode": 61}]
|
|
changes = detect_changes(old, new)
|
|
assert len(changes) == 1
|
|
assert "2026-03-12" in changes[0]
|
|
assert "rain" in changes[0].lower() or "precip" in changes[0].lower()
|
|
|
|
|
|
def test_parse_weather_card_data_returns_none_when_stale():
|
|
"""Should return None when cache is older than 24 hours."""
|
|
from datetime import datetime, timezone, timedelta
|
|
from unittest.mock import MagicMock
|
|
from fabledassistant.services.weather import parse_weather_card_data
|
|
|
|
cache = MagicMock()
|
|
cache.fetched_at = datetime.now(timezone.utc) - timedelta(hours=25)
|
|
cache.forecast_json = {}
|
|
assert parse_weather_card_data(cache) is None
|
|
|
|
|
|
def test_parse_weather_card_data_returns_card_schema():
|
|
"""Should return the correct metadata.weather schema for fresh cache."""
|
|
from datetime import datetime, timezone, timedelta, date
|
|
from unittest.mock import MagicMock
|
|
from fabledassistant.services.weather import parse_weather_card_data
|
|
|
|
today = date.today().isoformat()
|
|
yesterday = (date.today() - timedelta(days=1)).isoformat()
|
|
tomorrow = (date.today() + timedelta(days=1)).isoformat()
|
|
|
|
cache = MagicMock()
|
|
cache.fetched_at = datetime.now(timezone.utc)
|
|
cache.location_label = "Berlin, DE"
|
|
cache.forecast_json = {
|
|
"current_weather": {"temperature": 12.0, "weathercode": 2},
|
|
"daily": {
|
|
"time": [yesterday, today, tomorrow],
|
|
"temperature_2m_max": [14.0, 16.0, 18.0],
|
|
"temperature_2m_min": [9.0, 8.0, 10.0],
|
|
"precipitation_sum": [0.0, 0.0, 1.5],
|
|
"weathercode": [1, 2, 61],
|
|
"windspeed_10m_max": [10.0, 12.0, 8.0],
|
|
}
|
|
}
|
|
|
|
card = parse_weather_card_data(cache)
|
|
assert card is not None
|
|
assert card["current_temp"] == 12
|
|
assert card["condition"] == "Partly cloudy"
|
|
assert card["today_high"] == 16
|
|
assert card["today_low"] == 8
|
|
assert card["yesterday_high"] == 14
|
|
assert card["yesterday_low"] == 9
|
|
assert len(card["forecast"]) >= 1
|
|
assert card["location"] == "Berlin, DE"
|