from pathlib import Path from unittest.mock import MagicMock, patch import pytest import requests from planemapper.fetcher import FileFixtureFetcher, HttpFetcher _FIXTURES = Path(__file__).parent / "fixtures" / "aircraft_sample.json" _FULL_RESPONSE = { "aircraft": [ { "hex": "4ca7f2", "lat": 53.3498, "lon": -6.2603, "flight": "EIN123 ", "altitude": 12000, "category": "A3", "track": 270.0, "mlat": [], } ] } _DEFAULTS_RESPONSE = { "aircraft": [ { "hex": "aabbcc", "lat": 51.0, "lon": -1.0, } ] } _MLAT_RESPONSE = { "aircraft": [ { "hex": "dddddd", "lat": 52.0, "lon": -2.0, "mlat": ["lat", "lon"], } ] } def _mock_get(payload: dict) -> MagicMock: mock_resp = MagicMock() mock_resp.json.return_value = payload mock_resp.raise_for_status = MagicMock() return mock_resp def test_http_fetcher_full_response() -> None: with patch("planemapper.fetcher.requests.get", return_value=_mock_get(_FULL_RESPONSE)): aircraft = HttpFetcher().fetch() assert len(aircraft) == 1 a = aircraft[0] assert a.icao == "4ca7f2" assert a.lat == 53.3498 assert a.lon == -6.2603 assert a.callsign == "EIN123" assert a.altitude_ft == 12000 assert a.category == "A3" assert a.heading == 270.0 assert a.is_mlat is False assert a.is_stale is False def test_http_fetcher_missing_fields_use_defaults() -> None: with patch("planemapper.fetcher.requests.get", return_value=_mock_get(_DEFAULTS_RESPONSE)): aircraft = HttpFetcher().fetch() assert len(aircraft) == 1 a = aircraft[0] assert a.callsign == "" assert a.altitude_ft == 0 assert a.category == "" assert a.is_mlat is False def test_http_fetcher_altitude_ground_string() -> None: payload = {"aircraft": [{"hex": "aaa", "lat": 51.0, "lon": -1.0, "altitude": "ground"}]} with patch("planemapper.fetcher.requests.get", return_value=_mock_get(payload)): aircraft = HttpFetcher().fetch() assert aircraft[0].altitude_ft == 0 def test_http_fetcher_skips_entries_without_position() -> None: payload = {"aircraft": [{"hex": "nopos"}, {"hex": "haspos", "lat": 51.0, "lon": -1.0}]} with patch("planemapper.fetcher.requests.get", return_value=_mock_get(payload)): aircraft = HttpFetcher().fetch() assert len(aircraft) == 1 assert aircraft[0].icao == "haspos" def test_http_fetcher_propagates_timeout() -> None: with patch("planemapper.fetcher.requests.get", side_effect=requests.Timeout): with pytest.raises(requests.Timeout): HttpFetcher().fetch() def test_http_fetcher_mlat_flag() -> None: with patch("planemapper.fetcher.requests.get", return_value=_mock_get(_MLAT_RESPONSE)): aircraft = HttpFetcher().fetch() assert aircraft[0].is_mlat is True def test_file_fixture_fetcher_returns_aircraft() -> None: aircraft = FileFixtureFetcher(_FIXTURES).fetch() assert len(aircraft) == 4 def test_file_fixture_fetcher_no_network_call() -> None: with patch("planemapper.fetcher.requests.get") as mock_get: FileFixtureFetcher(_FIXTURES).fetch() mock_get.assert_not_called() def test_file_fixture_fetcher_mlat_aircraft() -> None: aircraft = FileFixtureFetcher(_FIXTURES).fetch() mlat_aircraft = [a for a in aircraft if a.is_mlat] assert len(mlat_aircraft) == 1 assert mlat_aircraft[0].icao == "4003c3"