feat: ship MVP itinerary optimization, weather, AI key prefs, and MCP tools

This commit is contained in:
2026-03-08 13:49:32 +00:00
parent 9eb0325c7a
commit 8c0637c518
25 changed files with 1888 additions and 511 deletions

View File

@@ -1,3 +1,108 @@
from django.test import TestCase
from datetime import timedelta
from unittest.mock import Mock, patch
# Create your tests here.
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.utils import timezone
from rest_framework.test import APIClient, APITestCase
User = get_user_model()
class WeatherEndpointTests(APITestCase):
def setUp(self):
self.user = User.objects.create_user(
username="weather-user",
email="weather@example.com",
password="password123",
)
self.client.force_authenticate(user=self.user)
cache.clear()
def test_daily_temperatures_rejects_too_many_days(self):
payload = {
"days": [
{"date": "2026-01-01", "latitude": 10, "longitude": 10}
for _ in range(61)
]
}
response = self.client.post(
"/api/weather/daily-temperatures/", payload, format="json"
)
self.assertEqual(response.status_code, 400)
self.assertIn("maximum", response.json().get("error", "").lower())
@patch("adventures.views.weather_view.requests.get")
def test_daily_temperatures_future_date_returns_unavailable_without_external_call(
self, mock_requests_get
):
future_date = (timezone.now().date() + timedelta(days=10)).isoformat()
response = self.client.post(
"/api/weather/daily-temperatures/",
{"days": [{"date": future_date, "latitude": 12.34, "longitude": 56.78}]},
format="json",
)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json()["results"][0],
{"date": future_date, "available": False, "temperature_c": None},
)
mock_requests_get.assert_not_called()
@patch("adventures.views.weather_view.requests.get")
def test_daily_temperatures_accepts_zero_lat_lon(self, mock_requests_get):
today = timezone.now().date().isoformat()
mocked_response = Mock()
mocked_response.raise_for_status.return_value = None
mocked_response.json.return_value = {
"daily": {
"temperature_2m_max": [20.0],
"temperature_2m_min": [10.0],
}
}
mock_requests_get.return_value = mocked_response
response = self.client.post(
"/api/weather/daily-temperatures/",
{"days": [{"date": today, "latitude": 0, "longitude": 0}]},
format="json",
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["results"][0]["date"], today)
self.assertTrue(response.json()["results"][0]["available"])
self.assertEqual(response.json()["results"][0]["temperature_c"], 15.0)
class RecommendationPhotoProxyValidationTests(APITestCase):
def setUp(self):
self.user = User.objects.create_user(
username="reco-user",
email="reco@example.com",
password="password123",
)
self.client.force_authenticate(user=self.user)
def test_google_photo_rejects_invalid_photo_name(self):
response = self.client.get(
"/api/recommendations/google-photo/?photo_name=invalid-photo-name"
)
self.assertEqual(response.status_code, 400)
def test_google_photo_rejects_trailing_newline_photo_name(self):
response = self.client.get(
"/api/recommendations/google-photo/?photo_name=places/abc/photos/def%0A"
)
self.assertEqual(response.status_code, 400)
class MCPAuthTests(APITestCase):
def test_mcp_unauthenticated_access_is_rejected(self):
unauthenticated_client = APIClient()
response = unauthenticated_client.post("/api/mcp", {}, format="json")
self.assertIn(response.status_code, [401, 403])