114 lines
4.3 KiB
Python
114 lines
4.3 KiB
Python
from unittest.mock import patch
|
||
|
||
from app.homelab.openmeteo import (
|
||
PRECIP_PROB_HINT,
|
||
RECOMMENDED_SYNC_DOMAINS,
|
||
RECOMMENDED_SYNC_VARIABLES,
|
||
SYNC_HINT,
|
||
_coverage_sufficient,
|
||
_field_coverage,
|
||
_hourly_start_index,
|
||
_local_needs_sync_hint,
|
||
build_weather_dashboard,
|
||
format_weather_snapshot,
|
||
weather_query_relevant,
|
||
)
|
||
|
||
|
||
def test_hourly_start_index_from_current():
|
||
times = ["2026-06-14T00:00", "2026-06-14T01:00", "2026-06-14T18:00", "2026-06-14T19:00"]
|
||
assert _hourly_start_index(times, "2026-06-14T18:15") == 2
|
||
|
||
|
||
def test_coverage_sufficient():
|
||
assert _coverage_sufficient({"current": ["temperature_2m"], "hourly": ["temperature_2m"]}) is False
|
||
assert _coverage_sufficient(
|
||
{
|
||
"current": ["temperature_2m", "weather_code", "wind_speed_10m"],
|
||
"hourly": ["temperature_2m", "weather_code"],
|
||
}
|
||
) is True
|
||
|
||
|
||
def test_field_coverage_partial():
|
||
raw = {
|
||
"current": {"time": "2026-06-14T18:15", "temperature_2m": 20.6, "weather_code": 2},
|
||
"hourly": {
|
||
"time": ["2026-06-14T18:00", "2026-06-14T19:00"],
|
||
"temperature_2m": [20.0, 19.5],
|
||
"precipitation": [0.0, 0.0],
|
||
"weather_code": [2, 3],
|
||
},
|
||
"daily": {
|
||
"time": ["2026-06-14", "2026-06-15"],
|
||
"temperature_2m_max": [21.0, 18.0],
|
||
"temperature_2m_min": [12.0, 10.0],
|
||
"weather_code": [2, 3],
|
||
},
|
||
}
|
||
coverage = _field_coverage(raw)
|
||
assert "temperature_2m" in coverage["current"]
|
||
assert "weather_code" in coverage["hourly"]
|
||
assert "temperature_2m_max" in coverage["daily"]
|
||
|
||
|
||
def test_local_needs_sync_hint():
|
||
assert _local_needs_sync_hint({"current": ["temperature_2m"], "hourly": ["temperature_2m"]}) is True
|
||
assert _local_needs_sync_hint(
|
||
{"current": ["temperature_2m", "weather_code"], "hourly": ["temperature_2m", "weather_code"]}
|
||
) is False
|
||
|
||
|
||
def test_weather_query_relevant():
|
||
assert weather_query_relevant("какая погода завтра")
|
||
assert not weather_query_relevant("напиши код на python")
|
||
|
||
|
||
def test_format_weather_snapshot_includes_tomorrow():
|
||
snap = {
|
||
"ok": True,
|
||
"location": "СПб",
|
||
"current": {"temperature_c": 20, "conditions": "ясно"},
|
||
"hourly": [],
|
||
"daily": [
|
||
{"label": "Сегодня", "temperature_min_c": 10, "temperature_max_c": 20, "conditions": "ясно"},
|
||
{"label": "Завтра", "temperature_min_c": 12, "temperature_max_c": 18, "conditions": "дождь"},
|
||
],
|
||
}
|
||
text = format_weather_snapshot(snap)
|
||
assert "Завтра:" in text
|
||
assert "None" not in text
|
||
|
||
|
||
def test_build_weather_dashboard_includes_daily():
|
||
fake_weather = {
|
||
"ok": True,
|
||
"location": "Test",
|
||
"data_source": "local",
|
||
"local_field_coverage": {"current": ["temperature_2m", "weather_code"], "hourly": ["temperature_2m"], "daily": []},
|
||
"field_coverage": {"current": ["temperature_2m"], "hourly": ["temperature_2m"], "daily": []},
|
||
"sync_hint": PRECIP_PROB_HINT,
|
||
"merged_fields": [],
|
||
"current": {"temperature_c": 10, "conditions": "ясно"},
|
||
"hourly": [],
|
||
"daily": [{"label": "Завтра", "temperature_min_c": 5, "temperature_max_c": 12, "conditions": "дождь"}],
|
||
}
|
||
with patch("app.homelab.openmeteo.OpenMeteoClient") as mock_cls:
|
||
client = mock_cls.return_value
|
||
client.fetch_forecast.return_value = fake_weather
|
||
client.rain_summary.return_value = "ok"
|
||
client.daily_summary.return_value = "Завтра: 5–12°C"
|
||
client.cache_status.return_value = {"source": "local", "has_data": True, "cached": True, "ttl_sec": 300}
|
||
client.location_name = "Test"
|
||
client.lat = 1.0
|
||
client.lon = 2.0
|
||
client.base_url = "http://local"
|
||
client.cache_ttl = 300
|
||
client.forecast_days = 7
|
||
result = build_weather_dashboard(days_ahead=7)
|
||
|
||
assert result["daily_summary"] == "Завтра: 5–12°C"
|
||
assert result["recommended_sync"]["domains"] == RECOMMENDED_SYNC_DOMAINS
|
||
assert result["recommended_sync"]["variables"] == RECOMMENDED_SYNC_VARIABLES
|
||
assert SYNC_HINT # constant exists
|