Decouple live weather reads from source collection

This commit is contained in:
2569718930@qq.com
2026-06-14 17:53:24 +08:00
parent a098a5ab20
commit e111031119
18 changed files with 2248 additions and 117 deletions
+76 -22
View File
@@ -129,6 +129,69 @@ def test_system_cache_status_requires_ops_admin(monkeypatch):
assert response.status_code in {401, 403, 503}
def test_system_priority_warm_enqueues_collector_refresh_without_direct_refresh(monkeypatch):
from web.services import system_api
tasks = []
enqueued = []
class _BackgroundTasks:
@staticmethod
def add_task(fn, *args, **kwargs):
tasks.append((fn, args, kwargs))
class _Cache:
@staticmethod
def enqueue_observation_refresh_request(**kwargs):
enqueued.append(kwargs)
return True
def fail_refresh(*_args, **_kwargs):
raise AssertionError("priority warm must enqueue collector refreshes")
monkeypatch.setattr(system_api.legacy_routes, "_assert_entitlement", lambda request: None)
monkeypatch.setattr(
system_api.legacy_routes,
"_select_priority_city_batches",
lambda timezone: {
"region": "asia",
"timezone": timezone,
"primary": ["shenzhen"],
"secondary": ["seoul"],
},
)
monkeypatch.setattr(system_api.legacy_routes, "_CACHE_DB", _Cache())
monkeypatch.setattr(system_api.legacy_routes, "_refresh_city_summary_cache", fail_refresh)
monkeypatch.setattr(system_api.legacy_routes, "_refresh_city_panel_cache", fail_refresh)
monkeypatch.setattr(system_api.legacy_routes, "_refresh_city_nearby_cache", fail_refresh)
monkeypatch.setattr(system_api.legacy_routes, "_refresh_city_market_cache", fail_refresh)
monkeypatch.setattr(system_api.legacy_routes, "_refresh_city_full_cache", fail_refresh)
payload = system_api.run_system_priority_warm(
object(),
_BackgroundTasks(),
timezone="Asia/Shanghai",
)
tasks[0][0](*tasks[0][1], **tasks[0][2])
assert payload["primary"] == ["shenzhen"]
assert payload["secondary"] == ["seoul"]
assert enqueued == [
{
"city": "shenzhen",
"kind": "panel",
"priority": "high",
"reason": "system_priority_warm",
},
{
"city": "seoul",
"kind": "panel",
"priority": "normal",
"reason": "system_priority_warm",
},
]
def test_standard_growth_funnel_events_are_trackable():
assert {
"landing_view",
@@ -1531,7 +1594,7 @@ def test_concurrent_city_detail_batch_requests_share_inflight_response(monkeypat
assert build_calls == 1
def test_concurrent_city_detail_requests_share_same_full_cache_refresh(monkeypatch):
def test_concurrent_cold_city_detail_requests_return_initializing_without_full_refresh(monkeypatch):
import asyncio
refresh_calls = 0
@@ -1587,8 +1650,9 @@ def test_concurrent_city_detail_requests_share_same_full_cache_refresh(monkeypat
results = asyncio.run(run_two_requests())
assert [item["city"] for item in results] == ["paris", "paris"]
assert refresh_calls == 1
assert build_calls == 1
assert [item["status"] for item in results] == ["initializing", "initializing"]
assert refresh_calls == 0
assert build_calls == 0
def test_stale_city_detail_uses_cached_full_payload_while_refreshing(monkeypatch):
@@ -1887,22 +1951,11 @@ def test_force_refresh_full_detail_returns_cached_payload_when_refresh_is_slow(m
assert refresh_calls == 1
def test_force_refresh_invalidates_short_city_detail_payload_cache(monkeypatch):
def test_force_refresh_cold_city_detail_returns_initializing_without_full_refresh(monkeypatch):
import asyncio
build_calls = 0
refreshed_payloads = [
{
"city": "paris",
"local_date": "2026-05-30",
"hourly": {"times": ["2026-05-30T00:00:00Z"], "temps": [20.0]},
},
{
"city": "paris",
"local_date": "2026-05-30",
"hourly": {"times": ["2026-05-30T00:00:00Z"], "temps": [21.0]},
},
]
refresh_calls = 0
class FakeCache:
def get_city_cache(self, kind, city):
@@ -1913,9 +1966,9 @@ def test_force_refresh_invalidates_short_city_detail_payload_cache(monkeypatch):
return fn(*args, **kwargs)
def refresh_full(city, force_refresh):
assert city == "paris"
assert refreshed_payloads
return refreshed_payloads.pop(0)
nonlocal refresh_calls
refresh_calls += 1
raise AssertionError("cold force refresh must not synchronously refresh full city data")
def build_detail(data, market_slug, target_date, resolution):
nonlocal build_calls
@@ -1947,9 +2000,10 @@ def test_force_refresh_invalidates_short_city_detail_payload_cache(monkeypatch):
),
)
assert first["live_temp"] == 20.0
assert second["live_temp"] == 21.0
assert build_calls == 2
assert first["status"] == "initializing"
assert second["status"] == "initializing"
assert refresh_calls == 0
assert build_calls == 0
def test_payment_runtime_endpoint_returns_shape():