Spaces:
Runtime error
Runtime error
| from datetime import date, time | |
| from fastapi.testclient import TestClient | |
| from PIL import Image | |
| from waterleaf.application import WaterleafApplication | |
| from waterleaf.models import CareProfile, LocationMatch, TaxonCandidate, WeatherDay | |
| from waterleaf.storage import GardenStore | |
| from waterleaf.web import create_web_app | |
| class FakeCare: | |
| def get_care(self, scientific_name): | |
| return CareProfile( | |
| scientific_name=scientific_name, | |
| common_name="English lavender", | |
| min_days=7, | |
| max_days=9, | |
| watering_label="Minimum", | |
| sunlight=["full sun"], | |
| ) | |
| class FakeWeather: | |
| def geocode(self, query): | |
| assert query == "Stockholm" | |
| return LocationMatch( | |
| display_name="Stockholm, Sweden", | |
| latitude=59.33, | |
| longitude=18.07, | |
| timezone="Europe/Stockholm", | |
| ) | |
| def forecast(self, latitude, longitude): | |
| return [ | |
| WeatherDay( | |
| date=date(2026, 6, 16), | |
| precipitation_mm=7.0, | |
| max_temperature_c=20.0, | |
| et0_mm=2.0, | |
| ) | |
| ] | |
| def test_application_saves_plan_and_exports_whole_garden(tmp_path): | |
| source = tmp_path / "lavender.png" | |
| Image.new("RGB", (300, 500), color=(90, 120, 60)).save(source) | |
| store = GardenStore(tmp_path / "waterleaf.sqlite3") | |
| application = WaterleafApplication( | |
| store=store, | |
| media_directory=tmp_path / "media", | |
| export_directory=tmp_path / "exports", | |
| public_base_url="https://waterleaf.example", | |
| care=FakeCare(), | |
| weather=FakeWeather(), | |
| ) | |
| candidate = TaxonCandidate( | |
| taxon_key="2925518", | |
| scientific_name="Lavandula angustifolia", | |
| common_name="English lavender", | |
| confidence=0.91, | |
| rationale="Purple spikes and narrow leaves", | |
| ) | |
| plan = application.preview_schedule( | |
| candidate=candidate, | |
| location_query="Stockholm", | |
| is_container=True, | |
| size_label="medium", | |
| start=date(2026, 6, 8), | |
| ) | |
| saved = application.save_plant( | |
| owner="alice", | |
| nickname="Patio lavender", | |
| candidate=candidate, | |
| source_image=source, | |
| preferred_time=time(7, 30), | |
| plan=plan, | |
| ) | |
| exported = application.export_garden("alice", generated_at="20260608T100000Z") | |
| assert saved.image_id | |
| assert store.get_schedule("alice", saved.id) | |
| content = exported.read_text() | |
| unfolded = content.replace("\n ", "") | |
| assert "SUMMARY:Water Patio lavender" in content | |
| assert f"https://waterleaf.example/plants/{saved.public_slug}" in content | |
| assert f"https://waterleaf.example/media/{saved.image_id}.jpg" in unfolded | |
| def test_public_profile_and_media_routes_exclude_private_location(tmp_path): | |
| source = tmp_path / "lavender.png" | |
| Image.new("RGB", (100, 100), color=(90, 120, 60)).save(source) | |
| store = GardenStore(tmp_path / "waterleaf.sqlite3") | |
| application = WaterleafApplication( | |
| store=store, | |
| media_directory=tmp_path / "media", | |
| export_directory=tmp_path / "exports", | |
| public_base_url="https://waterleaf.example", | |
| care=FakeCare(), | |
| weather=FakeWeather(), | |
| ) | |
| candidate = TaxonCandidate( | |
| taxon_key="2925518", | |
| scientific_name="Lavandula angustifolia", | |
| common_name="English lavender", | |
| ) | |
| plan = application.preview_schedule( | |
| candidate=candidate, | |
| location_query="Stockholm", | |
| is_container=True, | |
| size_label="medium", | |
| start=date(2026, 6, 8), | |
| ) | |
| saved = application.save_plant( | |
| owner="alice", | |
| nickname="Patio lavender", | |
| candidate=candidate, | |
| source_image=source, | |
| preferred_time=time(7, 30), | |
| plan=plan, | |
| ) | |
| client = TestClient(create_web_app(application, mount_ui=False)) | |
| health = client.get("/health") | |
| profile = client.get(f"/plants/{saved.public_slug}") | |
| media = client.get(f"/media/{saved.image_id}.jpg") | |
| assert health.json() == {"status": "ok"} | |
| assert profile.status_code == 200 | |
| assert "Patio lavender" in profile.text | |
| assert "Lavandula angustifolia" in profile.text | |
| assert "7-9 days" in profile.text | |
| assert "2026-06-" in profile.text | |
| assert "Stockholm" not in profile.text | |
| assert "alice" not in profile.text | |
| assert media.status_code == 200 | |
| assert media.headers["content-type"] == "image/jpeg" | |
| assert client.get("/media/../../private.jpg").status_code == 404 | |
| def test_application_delete_removes_unreferenced_media(tmp_path): | |
| source = tmp_path / "lavender.png" | |
| Image.new("RGB", (100, 100), color=(90, 120, 60)).save(source) | |
| store = GardenStore(tmp_path / "waterleaf.sqlite3") | |
| application = WaterleafApplication( | |
| store=store, | |
| media_directory=tmp_path / "media", | |
| export_directory=tmp_path / "exports", | |
| public_base_url="https://waterleaf.example", | |
| care=FakeCare(), | |
| weather=FakeWeather(), | |
| ) | |
| candidate = TaxonCandidate( | |
| taxon_key="2925518", | |
| scientific_name="Lavandula angustifolia", | |
| common_name="English lavender", | |
| ) | |
| plan = application.preview_schedule( | |
| candidate=candidate, | |
| location_query="Stockholm", | |
| is_container=True, | |
| size_label="medium", | |
| start=date(2026, 6, 8), | |
| ) | |
| first = application.save_plant( | |
| owner="alice", | |
| nickname="Patio lavender", | |
| candidate=candidate, | |
| source_image=source, | |
| preferred_time=time(7, 30), | |
| plan=plan, | |
| ) | |
| second = application.save_plant( | |
| owner="alice", | |
| nickname="Second lavender", | |
| candidate=candidate, | |
| source_image=source, | |
| preferred_time=time(7, 30), | |
| plan=plan, | |
| ) | |
| media_path = application.media_directory / f"{first.image_id}.jpg" | |
| assert first.image_id == second.image_id | |
| assert media_path.exists() | |
| assert application.delete_plant("alice", first.id) is True | |
| assert media_path.exists() | |
| assert application.delete_plant("alice", second.id) is True | |
| assert not media_path.exists() | |