from __future__ import annotations from pathlib import Path import pytest from device.manager import DeviceManager from host_agent.cloud_planner_client import CloudProxyToolCallingClient from host_agent.config import HostAgentConfig from host_agent.execution import create_execution_factories from runtime.ai_planner import AIPlanner from runtime.planner import Planner from runtime.task import TaskRunner from workflow.runner import WorkflowRunner from workflow.store import WorkflowStore def test_execution_factories_compose_existing_runtime_and_workflow(tmp_path) -> None: manager = DeviceManager() workflow_store = WorkflowStore(tmp_path / "workflows.sqlite3") factories = create_execution_factories( manager, workflow_store=workflow_store, ) task_runner = factories.task_runner_factory() workflow_runner = factories.workflow_runner_factory() assert isinstance(task_runner, TaskRunner) assert isinstance(workflow_runner, WorkflowRunner) assert workflow_runner.store is workflow_store assert isinstance(workflow_runner.task_runner_factory(), TaskRunner) def test_created_task_runner_defaults_to_ai_planner( tmp_path, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.delenv("AI_PLANNER_ENABLED", raising=False) manager = DeviceManager() factories = create_execution_factories( manager, workflow_store=WorkflowStore(tmp_path / "workflows.sqlite3") ) task_runner = factories.task_runner_factory() assert isinstance(task_runner.planner, AIPlanner) def test_created_task_runner_honors_explicit_ai_planner_opt_out( tmp_path, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.setenv("AI_PLANNER_ENABLED", "false") manager = DeviceManager() factories = create_execution_factories( manager, workflow_store=WorkflowStore(tmp_path / "workflows.sqlite3") ) task_runner = factories.task_runner_factory() assert type(task_runner.planner) is Planner def test_cloud_transport_builds_ai_planner_with_cloud_proxy_client( tmp_path, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.delenv("AI_PLANNER_ENABLED", raising=False) manager = DeviceManager() host_agent_config = HostAgentConfig( control_plane_url="https://control-plane.example", host_id="host-a", token="token-a", ai_planner_transport="cloud", ) factories = create_execution_factories( manager, workflow_store=WorkflowStore(tmp_path / "workflows.sqlite3"), host_agent_config=host_agent_config, ) task_runner = factories.task_runner_factory() assert isinstance(task_runner.planner, AIPlanner) assert isinstance(task_runner.planner.client, CloudProxyToolCallingClient) assert task_runner.planner.client.config is host_agent_config @pytest.mark.parametrize("transport", [None, "direct"]) def test_direct_transport_preserves_existing_local_provider_construction( tmp_path, monkeypatch: pytest.MonkeyPatch, transport: str | None ) -> None: monkeypatch.delenv("AI_PLANNER_ENABLED", raising=False) manager = DeviceManager() host_agent_config = HostAgentConfig( control_plane_url="https://control-plane.example", host_id="host-a", token="token-a", **({} if transport is None else {"ai_planner_transport": transport}), ) factories = create_execution_factories( manager, workflow_store=WorkflowStore(tmp_path / "workflows.sqlite3"), host_agent_config=host_agent_config, ) task_runner = factories.task_runner_factory() assert isinstance(task_runner.planner, AIPlanner) assert not isinstance(task_runner.planner.client, CloudProxyToolCallingClient) def test_runtime_owned_packages_do_not_import_host_or_cloud_concerns() -> None: root = Path(__file__).resolve().parents[3] forbidden = ("import cloud", "from cloud", "import host_agent", "from host_agent") for package in ("core", "device", "driver", "runtime", "tools"): for path in (root / package).rglob("*.py"): source = path.read_text(encoding="utf-8") assert not any(token in source for token in forbidden), path