import ast import importlib.resources from pathlib import Path REPO_ROOT = Path(__file__).parent.parent def test_required_toplevel_modules_exist() -> None: base = REPO_ROOT / "src" / "planemapper" for name in [ "__init__.py", "constants.py", "models.py", "main.py", "provision.py", "fetcher.py", "gpio_ctrl.py", "display.py", ]: assert (base / name).exists(), f"Missing: {name}" def test_provisioning_subpackage_exists() -> None: base = REPO_ROOT / "src" / "planemapper" / "provisioning" for name in [ "__init__.py", "portal.py", "location.py", "tiles.py", "airspace.py", "wifi.py", "config.py", ]: assert (base / name).exists(), f"Missing provisioning/{name}" def test_renderer_subpackage_exists() -> None: base = REPO_ROOT / "src" / "planemapper" / "renderer" for name in [ "__init__.py", "renderer.py", "projection.py", "basemap.py", "aircraft.py", "airspace.py", "colours.py", "icons.py", ]: assert (base / name).exists(), f"Missing renderer/{name}" def test_systemd_units_exist() -> None: systemd = REPO_ROOT / "systemd" assert (systemd / "planemapper-provision.service").exists() assert (systemd / "planemapper-radar.service").exists() def test_airports_csv_via_importlib_resources() -> None: ref = importlib.resources.files("planemapper.data").joinpath("airports.csv") content = ref.read_text(encoding="utf-8") assert len(content) > 0 assert "icao_code" in content or "ident" in content # OurAirports CSV header def test_constants_colours_complete() -> None: from planemapper import constants assert len(constants.ALTITUDE_COLOURS) == 6 assert len(constants.ALTITUDE_BANDS_FT) == 6 assert constants.DISPLAY_WIDTH == 800 assert constants.DISPLAY_HEIGHT == 480 assert constants.REFRESH_INTERVAL_S == 60 assert constants.FETCH_TIMEOUT_S == 5 def test_main_only_imports_config_from_provisioning() -> None: """main.py may import planemapper.provisioning.config (to read stored config) but must not import other provisioning sub-modules (portal, wifi, tiles, etc.).""" allowed = {"planemapper.provisioning.config"} main_path = REPO_ROOT / "src" / "planemapper" / "main.py" tree = ast.parse(main_path.read_text()) for node in ast.walk(tree): if isinstance(node, ast.ImportFrom) and node.module: if node.module.startswith("planemapper.provisioning"): assert node.module in allowed, f"main.py must not import from {node.module}"