104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
from django.apps import apps
|
|
from django.test import SimpleTestCase, TestCase, override_settings
|
|
|
|
from location_data.models import SoilLocation
|
|
from weather.adapters import MockWeatherAdapter, OpenMeteoWeatherAdapter
|
|
from weather.models import WeatherForecast
|
|
from weather.services import fetch_weather_from_api, update_weather_for_location
|
|
|
|
|
|
class MockWeatherAdapterTests(SimpleTestCase):
|
|
def setUp(self):
|
|
self.adapter = MockWeatherAdapter(delay_seconds=0)
|
|
|
|
def test_same_coordinate_returns_same_forecast(self):
|
|
first = self.adapter.fetch_forecast(35.71, 51.4)
|
|
second = self.adapter.fetch_forecast(35.71, 51.4)
|
|
|
|
self.assertEqual(first, second)
|
|
|
|
def test_nearby_coordinates_produce_nearby_forecast(self):
|
|
first = self.adapter.fetch_forecast(35.71, 51.4)
|
|
second = self.adapter.fetch_forecast(35.715, 51.405)
|
|
|
|
first_daily = first["daily"]
|
|
second_daily = second["daily"]
|
|
self.assertLess(
|
|
abs(first_daily["temperature_2m_mean"][0] - second_daily["temperature_2m_mean"][0]),
|
|
2.5,
|
|
)
|
|
self.assertLess(
|
|
abs(first_daily["relative_humidity_2m_mean"][0] - second_daily["relative_humidity_2m_mean"][0]),
|
|
8.0,
|
|
)
|
|
self.assertLess(
|
|
abs(first_daily["wind_speed_10m_max"][0] - second_daily["wind_speed_10m_max"][0]),
|
|
6.0,
|
|
)
|
|
|
|
def test_shape_matches_open_meteo_daily_contract(self):
|
|
forecast = self.adapter.fetch_forecast(35.71, 51.4)
|
|
daily = forecast["daily"]
|
|
|
|
self.assertEqual(len(daily["time"]), 7)
|
|
self.assertEqual(len(daily["temperature_2m_max"]), 7)
|
|
self.assertEqual(len(daily["weather_code"]), 7)
|
|
|
|
|
|
class WeatherAdapterSelectionTests(SimpleTestCase):
|
|
def tearDown(self):
|
|
apps.get_app_config("weather").__dict__.pop("weather_data_adapter", None)
|
|
|
|
@override_settings(WEATHER_DATA_PROVIDER="mock", WEATHER_MOCK_DELAY_SECONDS=0)
|
|
def test_app_config_returns_mock_adapter(self):
|
|
config = apps.get_app_config("weather")
|
|
config.__dict__.pop("weather_data_adapter", None)
|
|
|
|
adapter = config.get_weather_data_adapter()
|
|
|
|
self.assertIsInstance(adapter, MockWeatherAdapter)
|
|
|
|
@override_settings(WEATHER_DATA_PROVIDER="open-meteo", WEATHER_TIMEOUT_SECONDS=12)
|
|
def test_app_config_returns_live_adapter(self):
|
|
config = apps.get_app_config("weather")
|
|
config.__dict__.pop("weather_data_adapter", None)
|
|
|
|
adapter = config.get_weather_data_adapter()
|
|
|
|
self.assertIsInstance(adapter, OpenMeteoWeatherAdapter)
|
|
self.assertEqual(adapter.timeout, 12)
|
|
|
|
|
|
@override_settings(WEATHER_DATA_PROVIDER="mock", WEATHER_MOCK_DELAY_SECONDS=0)
|
|
class WeatherServiceTests(TestCase):
|
|
def setUp(self):
|
|
self.location = SoilLocation.objects.create(
|
|
latitude="35.710000",
|
|
longitude="51.400000",
|
|
)
|
|
|
|
def test_fetch_weather_from_api_uses_mock_provider(self):
|
|
payload = fetch_weather_from_api(35.71, 51.4)
|
|
|
|
self.assertIn("daily", payload)
|
|
self.assertEqual(len(payload["daily"]["time"]), 7)
|
|
|
|
def test_update_weather_for_location_persists_seven_days(self):
|
|
result = update_weather_for_location(self.location)
|
|
|
|
self.assertEqual(result["status"], "success")
|
|
self.assertEqual(result["days_updated"], 7)
|
|
self.assertEqual(
|
|
WeatherForecast.objects.filter(location=self.location).count(),
|
|
7,
|
|
)
|
|
self.assertTrue(
|
|
WeatherForecast.objects.filter(
|
|
location=self.location,
|
|
precipitation__isnull=False,
|
|
weather_code__isnull=False,
|
|
).exists()
|
|
)
|