# -*- coding: utf-8 -*- """Tests for the extracted agent service surface.""" from fastapi.testclient import TestClient from backend.apps.agent_service import create_app from backend.api import agents as agents_module def test_agent_service_routes_include_control_plane_endpoints(tmp_path): app = create_app(project_root=tmp_path) paths = {route.path for route in app.routes} assert "/health" in paths assert "/api/status" in paths assert "/api/workspaces" in paths assert "/api/guard/pending" in paths def test_agent_service_excludes_runtime_routes(tmp_path): app = create_app(project_root=tmp_path) paths = {route.path for route in app.routes} assert "/api/runtime/start" not in paths assert "/api/runtime/gateway/port" not in paths def test_agent_service_status_includes_scope_metadata(tmp_path): app = create_app(project_root=tmp_path) with TestClient(app) as client: response = client.get("/api/status") assert response.status_code == 200 payload = response.json() assert payload["scope"]["design_time_registry"]["root"] == str(tmp_path / "workspaces") assert payload["scope"]["runtime_assets"]["root"] == str(tmp_path / "runs") assert "runs/{run_id}" in payload["scope"]["agent_route_note"] def test_agent_service_read_routes(monkeypatch, tmp_path): class _FakeSkillsManager: project_root = tmp_path def get_agent_asset_dir(self, config_name, agent_id): return tmp_path / "runs" / config_name / "agents" / agent_id def resolve_agent_skill_names(self, config_name, agent_id, default_skills=None): return ["demo_skill"] def list_agent_skill_catalog(self, config_name, agent_id): return [ type( "Skill", (), { "skill_name": "demo_skill", "name": "Demo Skill", "description": "demo", "version": "1.0.0", "source": "builtin", "tools": [], }, )() ] def load_agent_skill_document(self, config_name, agent_id, skill_name): return {"skill_name": skill_name, "content": "# demo"} class _FakeWorkspaceManager: def load_agent_file(self, config_name, agent_id, filename): return f"{config_name}:{agent_id}:{filename}" monkeypatch.setattr(agents_module, "load_agent_profiles", lambda: {"portfolio_manager": {"skills": ["demo_skill"]}}) monkeypatch.setattr(agents_module, "get_agent_model_info", lambda agent_id: ("deepseek-v3.2", "DASHSCOPE")) monkeypatch.setattr( agents_module, "load_agent_workspace_config", lambda path: type( "Cfg", (), { "active_tool_groups": ["portfolio_ops"], "disabled_tool_groups": [], "enabled_skills": [], "disabled_skills": [], "prompt_files": ["SOUL.md", "MEMORY.md"], }, )(), ) monkeypatch.setattr( agents_module, "get_bootstrap_config_for_run", lambda project_root, config_name: type("Bootstrap", (), {"agent_override": lambda self, agent_id: {}})(), ) app = create_app(project_root=tmp_path) app.dependency_overrides[agents_module.get_skills_manager] = lambda: _FakeSkillsManager() app.dependency_overrides[agents_module.get_workspace_manager] = lambda: _FakeWorkspaceManager() with TestClient(app) as client: profile = client.get("/api/workspaces/demo/agents/portfolio_manager/profile") skills = client.get("/api/workspaces/demo/agents/portfolio_manager/skills") detail = client.get("/api/workspaces/demo/agents/portfolio_manager/skills/demo_skill") workspace_file = client.get("/api/workspaces/demo/agents/portfolio_manager/files/MEMORY.md") assert profile.status_code == 200 assert profile.json()["profile"]["model_name"] == "deepseek-v3.2" assert profile.json()["scope_type"] == "runtime_run" assert skills.status_code == 200 assert skills.json()["skills"][0]["skill_name"] == "demo_skill" assert skills.json()["scope_type"] == "runtime_run" assert detail.status_code == 200 assert detail.json()["skill"]["content"] == "# demo" assert detail.json()["scope_type"] == "runtime_run" assert workspace_file.status_code == 200 assert workspace_file.json()["content"] == "demo:portfolio_manager:MEMORY.md" assert workspace_file.json()["scope_type"] == "runtime_run" assert "runs/" in workspace_file.json()["scope_note"]