""" server-26#159: a citywide/patched feed can be received far from its own coverage area — "New York City - NYPD Citywide 2 Patch" was ~56km from the receiving node, well past geocode_max_km (40km). Real, correctly-geocoded addresses on that talkgroup were rejected by intelligence._geocode_location's node-distance sanity check every time, so location_coords never populated for the whole system: location_proximity correlation was permanently dead there, and the same real event reported at two nearby addresses two minutes apart became two separate incidents instead of one. `trust_named_region` fixes this narrowly: the node-distance check is a proxy for "is this plausible" that only makes sense when the node's own position is the best guess we have at the area. It must not apply when the query already names a different region on its own terms (operator-set area_context, or a municipality parsed straight from the talkgroup's own name) — and it must never touch the anchor path, whose own radius is always authoritative. """ from unittest.mock import patch import pytest import app.internal.intelligence as intel from app.config import settings def _maps_result(lat: float, lng: float, location_type: str = "ROOFTOP"): payload = { "status": "OK", "results": [{ "geometry": { "location": {"lat": lat, "lng": lng}, "location_type": location_type, }, }], } class _Resp: def raise_for_status(self): pass def json(self): return payload class _Client: async def __aenter__(self): return self async def __aexit__(self, *a): return False async def get(self, *a, **k): return _Resp() return patch("httpx.AsyncClient", lambda *a, **k: _Client()) @pytest.fixture(autouse=True) def _api_key(): # intelligence.py imports settings locally per-function (`from app.config # import settings`), which binds the same cached singleton — patching the # module-level object here reaches it, but `intel.settings` itself does # not exist as an attribute. with patch.object(settings, "google_maps_api_key", "test-key"): yield # Node at (0, 0); result at (1, 0) is ~111km away — well past the 40km default. NODE_LAT, NODE_LON = 0.0, 0.0 FAR_LAT, FAR_LNG = 1.0, 0.0 @pytest.mark.asyncio async def test_named_region_geocode_accepted_beyond_node_distance(): with _maps_result(FAR_LAT, FAR_LNG): coords = await intel._geocode_location( "1108 Jackson Avenue, New York City - NYPD Citywide 2 Patch", node_lat=NODE_LAT, node_lon=NODE_LON, trust_named_region=True, ) assert coords == {"lat": FAR_LAT, "lng": FAR_LNG} @pytest.mark.asyncio async def test_local_geocode_still_rejected_beyond_node_distance_without_named_region(): """Regression guard: a bare street name with no named region still uses the node as its only plausibility check, exactly as before this fix.""" with _maps_result(FAR_LAT, FAR_LNG): coords = await intel._geocode_location( "Main Street", node_lat=NODE_LAT, node_lon=NODE_LON, trust_named_region=False, ) assert coords is None @pytest.mark.asyncio async def test_anchor_path_ignores_trust_named_region(): """The anchor's own radius is always authoritative — trust_named_region is only a statement about the node fallback, never a way to widen an anchor that was itself deliberately sized to discriminate.""" anchor = {"lat": NODE_LAT, "lng": NODE_LON, "radius_km": 10.0} with _maps_result(FAR_LAT, FAR_LNG): coords = await intel._geocode_location( "1108 Jackson Avenue, New York City - NYPD Citywide 2 Patch", node_lat=NODE_LAT, node_lon=NODE_LON, anchor=anchor, trust_named_region=True, ) assert coords is None @pytest.mark.asyncio async def test_named_region_geocode_within_node_distance_is_unaffected(): """A close result is accepted the same way regardless of the flag.""" near_lat, near_lng = 0.05, 0.0 # ~5.5km from the node with _maps_result(near_lat, near_lng): coords = await intel._geocode_location( "Main Street, Ossining, New York", node_lat=NODE_LAT, node_lon=NODE_LON, trust_named_region=True, ) assert coords == {"lat": near_lat, "lng": near_lng} @pytest.mark.asyncio async def test_imprecise_result_still_rejected_regardless_of_trust(): """trust_named_region relaxes the distance check only — the location_type precision filter (server-26#37) still applies unconditionally.""" with _maps_result(FAR_LAT, FAR_LNG, location_type="APPROXIMATE"): coords = await intel._geocode_location( "1108 Jackson Avenue, New York City - NYPD Citywide 2 Patch", node_lat=NODE_LAT, node_lon=NODE_LON, trust_named_region=True, ) assert coords is None # --------------------------------------------------------------------------- # _location_query_parts — pure query assembly, no HTTP involved # --------------------------------------------------------------------------- def test_operator_configured_area_wins_and_is_named_region(): parts, named = intel._location_query_parts( "High Street", {"municipality": "Yorktown", "state": "New York"}, "Tac 1", node_state="New York", node_county="Westchester", ) assert parts == ["High Street", "Yorktown", "New York"] assert named is True def test_local_talkgroup_name_gets_node_state_but_not_node_county(): """ "Ossining PD" is genuinely local to the node, so appending the node's own state is correct. Its COUNTY is dropped even here — server-26#159's fix applies uniformly once a municipality is derived, since there is no way to tell "local" and "distant-but-node-adjacent" apart from the string alone, and the county was never necessary for a bare municipality name that already disambiguates via the state. """ parts, named = intel._location_query_parts( "High Street", {}, "Ossining PD", node_state="New York", node_county="Westchester", ) assert parts == ["High Street", "Ossining", "New York"] assert named is True def test_citywide_patched_feed_does_not_get_the_nodes_county_grafted_on(): """ server-26#159's actual production case: the talkgroup names its own (distant) region, so the node's county (Westchester, ~56km away) must not be appended — it would make the query self-contradictory ("...New York City..., Westchester, New York") and risks degrading the geocode result's precision independently of the distance check this issue also fixes. """ parts, named = intel._location_query_parts( "1108 Jackson Avenue", {}, "New York City - NYPD Citywide 2 Patch", node_state="New York", node_county="Westchester", ) assert "Westchester" not in parts assert parts == ["1108 Jackson Avenue", "New York City - NYPD Citywide 2 Patch", "New York"] assert named is True def test_uninformative_talkgroup_name_falls_back_to_node_county_and_state(): """A tactical channel or bare code gives _municipality_from_tg nothing — the only remaining evidence really is where the node sits, so the original node-county-and-state fallback is preserved for this case.""" parts, named = intel._location_query_parts( "High Street", {}, "Tac 1", node_state="New York", node_county="Westchester", ) assert parts == ["High Street", "Westchester", "New York"] assert named is False