def test_canonical_engine_prefers_settlement_station_over_later_nearby_row(): from web.services.canonical_engine import build_canonical_temperature_from_observations canonical = build_canonical_temperature_from_observations( "shenzhen", [ { "source": "hko_obs", "city": "shenzhen", "station_code": "HKO", "station_name": "Hong Kong Observatory", "value": 27.6, "value_unit": "c", "observed_at": "2026-06-14T01:00:00+00:00", "fetched_at": "2026-06-14T01:05:00+00:00", "status": "ok", "payload": {"source_label": "HKO"}, "updated_at_ts": 2000.0, }, { "source": "hko_obs", "city": "shenzhen", "station_code": "LFS", "station_name": "Lau Fau Shan", "value": 28.1, "value_unit": "c", "observed_at": "2026-06-14T01:00:00+00:00", "fetched_at": "2026-06-14T01:05:00+00:00", "status": "ok", "payload": {"source_label": "HKO"}, "updated_at_ts": 1000.0, }, ], ) assert canonical is not None assert canonical["city"] == "shenzhen" assert canonical["value"] == 28.1 assert canonical["source"] == "hko_obs" assert canonical["source_role"] == "settlement_official" assert canonical["station_code"] == "LFS" assert canonical["station_name"] == "Lau Fau Shan" assert canonical["freshness_status"] == "fresh" assert canonical["freshness_sec"] == 300 def test_canonical_engine_ignores_failed_latest_rows(): from web.services.canonical_engine import build_canonical_temperature_from_observations canonical = build_canonical_temperature_from_observations( "qingdao", [ { "source": "amsc_awos", "city": "qingdao", "station_code": "ZSQD", "value": None, "value_unit": "c", "observed_at": "", "fetched_at": "2026-06-14T01:05:00+00:00", "status": "timeout", "payload": {"error": "timeout"}, "updated_at_ts": 2000.0, }, { "source": "madis_hfmetar", "city": "qingdao", "station_code": "ZSQD", "station_name": "Qingdao Jiaodong", "value": 24.0, "value_unit": "c", "observed_at": "2026-06-14T01:00:00+00:00", "fetched_at": "2026-06-14T01:05:00+00:00", "status": "ok", "payload": {"source_label": "MADIS HFMETAR"}, "updated_at_ts": 1000.0, }, ], ) assert canonical is not None assert canonical["source"] == "madis_hfmetar" assert canonical["value"] == 24.0 def test_canonical_engine_prefers_mgm_over_metar_for_turkish_city(): from web.services.canonical_engine import build_canonical_temperature_from_observations canonical = build_canonical_temperature_from_observations( "ankara", [ { "source": "metar", "city": "ankara", "station_code": "LTAC", "station_name": "Ankara Esenboga", "value": 14.0, "value_unit": "c", "observed_at": "2026-06-15T14:20:00+00:00", "fetched_at": "2026-06-15T14:21:00+00:00", "status": "ok", "payload": {"source_label": "METAR"}, "updated_at_ts": 10.0, }, { "source": "mgm", "city": "ankara", "station_code": "17128", "station_name": "Esenboga Airport", "value": 19.0, "value_unit": "c", "observed_at": "2026-06-15T14:20:00+00:00", "fetched_at": "2026-06-15T14:21:00+00:00", "status": "ok", "payload": {"source_label": "MGM"}, "updated_at_ts": 9.0, }, ], ) assert canonical is not None assert canonical["source"] == "mgm" assert canonical["source_label"] == "MGM" assert canonical["value"] == 19.0 def test_canonical_engine_builds_realtime_event_from_canonical(): from web.services.canonical_engine import build_realtime_event_from_canonical event = build_realtime_event_from_canonical( { "city": "shenzhen", "value": 28.1, "temp_symbol": "°C", "source": "hko_obs", "station_code": "LFS", "station_name": "Lau Fau Shan", "observed_at": "2026-06-14T01:00:00+00:00", "freshness_sec": 300, "freshness_status": "fresh", "confidence": 0.9, "max_so_far": 28.6, "signed_gap": 1.2, "gap_to_target": -1.2, "touch_distance": 0.0, "edge": 0.07, "edge_percent": 7.0, } ) assert event is not None assert event["type"] == "city_observation_patch.v1" assert event["city"] == "shenzhen" assert event["source"] == "hko_obs" assert event["obs_time"] == "2026-06-14T01:00:00Z" assert event["payload"]["temp"] == 28.1 assert event["payload"]["station_code"] == "LFS" assert event["payload"]["station_label"] == "Lau Fau Shan" assert event["payload"]["unit"] == "celsius" assert event["payload"]["freshness_status"] == "fresh" assert event["payload"]["confidence"] == 0.9 assert event["payload"]["max_so_far"] == 28.6 assert event["payload"]["signed_gap"] == 1.2 assert event["payload"]["gap_to_target"] == -1.2 assert event["payload"]["touch_distance"] == 0.0 assert event["payload"]["edge"] == 0.07 assert event["payload"]["edge_percent"] == 7.0