"""Smoke test that console templates and assets ship inside the Runtime wheel. Builds ``device-agent-runtime`` into a temporary directory, installs it into an isolated venv that cannot reach the source checkout, and asserts the packaged ``api`` package carries the Jinja2 templates and static assets needed by ``/ui/``. This guards against setuptools package-data regressions that would let the console work from an editable checkout but break from a real install. """ from __future__ import annotations import subprocess import venv from pathlib import Path import pytest def _run(cmd: list[str], *, cwd: Path | None = None) -> str: return subprocess.check_output( cmd, cwd=cwd, stderr=subprocess.STDOUT, text=True, ) @pytest.mark.integration def test_runtime_wheel_packages_console_templates_and_assets(tmp_path: Path) -> None: repo_root = Path(__file__).resolve().parent.parent wheel_dir = tmp_path / "wheels" wheel_dir.mkdir() _run( ["uv", "build", "--package", "device-agent-runtime", "--wheel", "--no-sources"], cwd=repo_root, ) wheels = list(repo_root.glob("dist/*.whl")) assert wheels, "uv build did not produce a wheel" wheel_path = wheels[0] venv_dir = tmp_path / "venv" venv.create(venv_dir, with_pip=True, clear=True) pip = str(venv_dir / "Scripts" / "pip.exe") if not Path(pip).exists(): pip = str(venv_dir / "bin" / "pip") _run([pip, "install", str(wheel_path)], cwd=tmp_path) python = str(venv_dir / "Scripts" / "python.exe") if not Path(python).exists(): python = str(venv_dir / "bin" / "python") probe = _run( [ python, "-c", ( "from importlib.resources import files; " "api_root = files('api'); " "templates = sorted(p.name for p in " "(api_root / 'templates' / 'runtime_console').iterdir()); " "assets = sorted(p.name for p in " "(api_root / 'static' / 'runtime_console').iterdir()); " "print(','.join(templates)); " "print(','.join(assets))" ), ], cwd=tmp_path, ) template_names, asset_names = probe.strip().splitlines() assert "base.html" in template_names assert "dashboard.html" in template_names assert "config.html" in template_names assert "console.css" in asset_names assert "dashboard.js" in asset_names # Clean up the build artifact so it does not leak into the working tree. for wheel in wheels: wheel.unlink()