o2o-site-AEO/solution/backend/tests/test_weather_api.py
Mina Choi 328d9e18ee [feat] solution: 지역 이야기 생성 · 발행본 섹션 손질 · 마이그레이션 주석 축약
- 지역 이야기(가요·인물·연표·엽서·퀴즈) 생성 경로: story_service · grounding/story ·
  section_prompts. 지금까지 만들 자리가 없어 시안에만 손으로 넣은 3만 자였다
- 발행본 섹션: ItinerarySection · Carousel 레일 자동재생(use-rail-autoplay) ·
  Festival · LocalGuide · Weather · Gallery · Header/Footer
- 목업 payload 를 payloads-mockup/ 으로 분리 — 발행 대상과 섞이지 않게
- DB 새 구조 후속: site_payload · local_content_crud 조인 정리 · 테스트
- 마이그레이션 주석 축약: 9개 파일 합계 주석 비율 48% → 25%.
  실측과 밟은 함정만 남기고 논증은 커밋 메시지로 옮겼다

검증: site·frontend 빌드 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:36:00 +09:00

62 lines
2.0 KiB
Python

from datetime import datetime, timedelta, timezone
from common.enums import ErrorType
from services.external.open_meteo import OpenMeteoRequestFailed
WEATHER = {
"temperature": 24.3,
"weather_code": 1,
"wind_speed": 3.2,
"observed_at": "2026-08-27T14:00",
"timezone": "Asia/Seoul",
"latitude": 33.46,
"longitude": 126.31,
}
async def test_weather_uses_region_cache(client, monkeypatch):
calls = 0
async def fetch(_lat, _lon):
nonlocal calls
calls += 1
return WEATHER
monkeypatch.setattr("services.local_content_service.fetch_current_weather", fetch)
params = {"region_code": "50110253", "latitude": 33.46, "longitude": 126.31}
first = (await client.get("/v1/local/weather", params=params)).json()
second = (await client.get("/v1/local/weather", params=params)).json()
assert first["weather"]["temperature"] == 24.3
assert first["cached"] is False
assert second["cached"] is True
assert calls == 1
async def test_weather_returns_stale_cache_when_upstream_fails(client, db_engine, monkeypatch):
params = {"region_code": "50110253", "latitude": 33.46, "longitude": 126.31}
monkeypatch.setattr("services.local_content_service.fetch_current_weather", lambda *_: _async_value(WEATHER))
await client.get("/v1/local/weather", params=params)
async with db_engine.begin() as conn:
from sqlalchemy import text
await conn.execute(text("UPDATE area_contents SET expires_at = :expired"), {
"expired": datetime.now(timezone.utc) - timedelta(minutes=1)
})
async def fail(*_args):
raise OpenMeteoRequestFailed("fail")
monkeypatch.setattr("services.local_content_service.fetch_current_weather", fail)
body = (await client.get("/v1/local/weather", params=params)).json()
assert body["result"]["code"] == ErrorType.SUCCESS.value
assert body["cached"] is True
assert body["stale"] is True
assert body["weather"]["temperature"] == 24.3
async def _async_value(value):
return value