"""Cold imports and runtime annotations must work on the installer's Python.""" from importlib import import_module import inspect from pathlib import Path import subprocess import sys from textwrap import dedent from typing import get_type_hints import pytest @pytest.mark.parametrize( ("module_name", "class_name"), [ ("artemis.exploration.store", "ExplorationStore"), ("artemis.exploration.demonstrations", "Demonstrations"), ("apps.admin_console.services.favorites", "FavoriteService"), ], ) def test_startup_service_annotations_do_not_shadow_builtin_collections(module_name, class_name): service = getattr(import_module(module_name), class_name) # Python 3.14 defers these errors until annotations are read. Evaluating them # also detects the import failure seen immediately on Python 3.12 and 3.13. assert get_type_hints(service.list)["return"] == list[dict] for method in vars(service).values(): if inspect.isfunction(method): get_type_hints(method) def test_ui_server_cold_import_serves_empty_workspace(tmp_path, monkeypatch): monkeypatch.setenv("ARTEMIS_APP_DIR", str(tmp_path / "app")) monkeypatch.setenv("ARTEMIS_TRACES_DIR", str(tmp_path / "traces")) code = dedent(""" from fastapi.testclient import TestClient from apps.admin_console.server import app # No lifespan: validate startup imports and HTTP routes without device # discovery, background workers, or a running user's service. client = TestClient(app, base_url="http://localhost") try: for path in ("/api/automation/favorites", "/api/explorations"): response = client.get(path) assert response.status_code == 200, response.text assert response.json() == [], response.text response = client.get("/openapi.json") assert response.status_code == 200, response.text assert "/api/explorations" in response.json()["paths"] finally: client.close() """) result = subprocess.run( [sys.executable, "-X", "utf8", "-c", code], cwd=Path(__file__).resolve().parents[2], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert result.returncode == 0, result.stdout + result.stderr