banyaro/tests/test_diary_location_enrich.py
rene 140140f690 Tagebuch: manuelle Positionierung reichert POIs + Wetter an (v1305)
Bisher holte nur der Create-/Foto-EXIF-Pfad Wetter+POIs. Wer einem Eintrag
nachträglich (Edit) einen Standort gab, bekam nichts. Jetzt: GPS neu/geändert
im Update-Handler -> POIs immer + Wetter fürs Eintragsdatum.

- weather.get_weather_for_date(): heute -> aktuelles Wetter; Vergangenheit ->
  stündliche Historie (Open-Meteo Forecast <=90 Tage, sonst Archive-API),
  Stunde aus created_at. Gleiche Dict-Struktur wie get_weather_for_location.
- diary.update_diary(): erfasst alten GPS-Stand, reichert nach DB-Commit an
  (async), schreibt nur erfolgreich geholte Felder (kein Datenverlust bei
  API-Fehler), identische Koordinaten -> kein erneuter Abruf.
- Tests: tests/test_diary_location_enrich.py (Anreicherung, kein GPS=kein
  Abruf, Resave ohne Re-Fetch).
2026-06-18 21:13:47 +02:00

95 lines
3.7 KiB
Python

"""Nachträgliches Positionieren eines Tagebucheintrags reichert POIs + Wetter an.
Deckt die Lücke ab: Der PATCH-/Update-Pfad holte bisher kein Wetter/POI, wenn
man einem Eintrag (z.B. Indoor-Foto ohne EXIF-GPS) nachträglich einen Standort
gibt. Jetzt: GPS neu gesetzt/geändert → POIs (immer) + Wetter fürs Eintragsdatum
(historisch korrekt via get_weather_for_date).
Wetter- und POI-Fetch sind gestubbt (kein echtes Netzwerk im Test).
"""
from __future__ import annotations
import json
import pytest
@pytest.fixture
def stub_enrich(monkeypatch, app):
# WICHTIG: routes.diary erst HIER importieren (nach dem app-Fixture, das
# DB_PATH setzt) — ein Top-Level-Import würde database mit dem Default-Pfad
# binden und die ganze Test-Session vergiften.
import routes.diary as diary_mod
calls = {}
async def _fake_weather(lat, lon, datum, hour=None):
calls["weather"] = {"lat": lat, "lon": lon, "datum": datum, "hour": hour}
return {"temp_c": 18.5, "weathercode": 61, "desc": "Regen",
"is_day": True, "historical": True}
async def _fake_pois(lat, lon, limit=5):
calls["poi"] = {"lat": lat, "lon": lon}
return [{"name": "Stadtpark", "type": "park", "distance_m": 120}]
monkeypatch.setattr(diary_mod.weather_mod, "get_weather_for_date", _fake_weather)
monkeypatch.setattr(diary_mod, "_fetch_pois_for_coords", _fake_pois)
return calls
def _create_without_gps(client, user, dog, datum="2026-06-12"):
r = client.post(
f"/api/dogs/{dog['id']}/diary",
headers=user["headers"],
json={"titel": "Ohne Standort", "text": "Indoor-Eintrag ohne GPS.", "datum": datum},
)
assert r.status_code == 201, r.text
e = r.json()
assert not e.get("weather_json") and not e.get("poi_json")
return e
def _loads(v):
return json.loads(v) if isinstance(v, str) else v
def test_manual_position_enriches_weather_and_pois(client, user, dog, stub_enrich):
entry = _create_without_gps(client, user, dog)
r = client.patch(
f"/api/dogs/{dog['id']}/diary/{entry['id']}",
headers=user["headers"],
json={"gps_lat": 48.137, "gps_lon": 11.575, "location_name": "München"},
)
assert r.status_code == 200, r.text
out = r.json()
w = _loads(out["weather_json"])
p = _loads(out["poi_json"])
assert w["weathercode"] == 61
assert p[0]["name"] == "Stadtpark"
assert out["location_name"] == "München"
# Wetter wurde fürs EINTRAGSDATUM geholt (historisch korrekt), nicht "heute".
assert stub_enrich["weather"]["datum"] == "2026-06-12"
def test_edit_without_gps_does_not_enrich(client, user, dog, stub_enrich):
entry = _create_without_gps(client, user, dog)
r = client.patch(
f"/api/dogs/{dog['id']}/diary/{entry['id']}",
headers=user["headers"],
json={"titel": "Nur Titel geändert"},
)
assert r.status_code == 200, r.text
assert "weather" not in stub_enrich and "poi" not in stub_enrich
def test_resave_same_coords_does_not_refetch(client, user, dog, stub_enrich):
entry = _create_without_gps(client, user, dog)
# 1. Positionierung → reichert an
client.patch(f"/api/dogs/{dog['id']}/diary/{entry['id']}", headers=user["headers"],
json={"gps_lat": 48.137, "gps_lon": 11.575})
assert "weather" in stub_enrich
stub_enrich.clear()
# erneut speichern mit IDENTISCHEN Koordinaten → keine erneute Anreicherung
r = client.patch(f"/api/dogs/{dog['id']}/diary/{entry['id']}", headers=user["headers"],
json={"gps_lat": 48.137, "gps_lon": 11.575})
assert r.status_code == 200
assert "weather" not in stub_enrich and "poi" not in stub_enrich