mirror of
https://github.com/TencentCloud/Octop.git
synced 2026-10-21 06:29:24 +08:00
* refactor(agents): group thin modules into domain subpackages Move plugin helpers, memory, settings stores, persona/MBTI, and avatar bootstrap code under plugins/, memory/, settings/, persona/, and experts/ while keeping top-level import shims so existing call sites stay stable. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(agents): drop compatibility shims after subpackage move Call sites already import the new package paths; remove the top-level re-export stubs so the agents tree only shows real modules. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(agents): move thread helpers into threads/ Group artifact shaping, fork, and context breakdown under agents/threads so top-level only keeps lifecycle/config adapters. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(agents): fold profile/runtime/tool catalog into settings/ Move the remaining config-surface helpers under settings/ and drop the skill_package id-list aliases in favor of dump/parse_id_list. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(agents): group workspace_dir and execute_env under workspace/ Keep path resolution and harness execute-env injection together as one workspace package; leave only manager and conversation_mode at top level. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(infra): fix history/SkillHub ownership and drop thin providers/ Move history_projection under history/, share SkillHub host/limits and route expert skill downloads through skills.skillhub_market, and fold Codex OAuth helpers into agents.providers to remove the tiny providers/ package. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: sync AGENTS.md and guides with infra/agents layout Reflect subpackages (settings/workspace/threads/persona, history/, skills/) and drop stale runtime.py / mbti_profiles paths after the agents reorg. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: jubaoliang <jubaoliang@tencent.com> Co-authored-by: Cursor <cursoragent@cursor.com>
116 lines
3.8 KiB
Python
116 lines
3.8 KiB
Python
"""Unit tests for default-agent bootstrap helper."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from octop.infra.agents.experts.catalog import ExpertCatalog, default_library_root
|
|
from octop.infra.agents.experts.default_agent import (
|
|
DEFAULT_EXPERT_ID,
|
|
SETUP_DEFAULT_AGENT_ID,
|
|
bootstrap_default_agent,
|
|
default_home_local_backend,
|
|
)
|
|
from octop.infra.errors import OctopError
|
|
from octop.infra.utils.host_dirs import host_fs_tree_root
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def catalog() -> ExpertCatalog:
|
|
cat = ExpertCatalog(default_library_root())
|
|
cat.refresh()
|
|
return cat
|
|
|
|
|
|
async def test_bootstrap_skips_when_user_has_agents(catalog: ExpertCatalog) -> None:
|
|
registry = MagicMock()
|
|
registry.list_agents.return_value = [object()]
|
|
registry.get_row.return_value = None
|
|
registry.create = AsyncMock()
|
|
result = await bootstrap_default_agent(registry, catalog, user_id=2, locale="zh", agent_id=None)
|
|
assert result is None
|
|
registry.create.assert_not_called()
|
|
|
|
|
|
async def test_bootstrap_skips_when_main_exists(catalog: ExpertCatalog) -> None:
|
|
registry = MagicMock()
|
|
registry.get_row.return_value = object()
|
|
registry.list_agents.return_value = []
|
|
registry.create = AsyncMock()
|
|
result = await bootstrap_default_agent(
|
|
registry,
|
|
catalog,
|
|
user_id=1,
|
|
locale="zh",
|
|
agent_id=SETUP_DEFAULT_AGENT_ID,
|
|
)
|
|
assert result is None
|
|
registry.create.assert_not_called()
|
|
|
|
|
|
async def test_bootstrap_creates_general_assistant(catalog: ExpertCatalog) -> None:
|
|
registry = MagicMock()
|
|
registry.get_row.return_value = None
|
|
registry.list_agents.return_value = []
|
|
created = object()
|
|
registry.create = AsyncMock(return_value=created)
|
|
result = await bootstrap_default_agent(registry, catalog, user_id=3, locale="en", agent_id=None)
|
|
assert result is created
|
|
registry.create.assert_awaited_once()
|
|
spec = registry.create.await_args.args[0]
|
|
assert spec.user_id == 3
|
|
assert spec.agent_id is None
|
|
assert spec.template_name == DEFAULT_EXPERT_ID
|
|
assert spec.config["backend"] == default_home_local_backend()
|
|
assert spec.config["backend"]["root_dir"] == host_fs_tree_root()
|
|
assert registry.create.await_args.kwargs["defer_bootstrap"] is True
|
|
|
|
|
|
async def test_bootstrap_main_uses_fs_root_backend(catalog: ExpertCatalog) -> None:
|
|
registry = MagicMock()
|
|
registry.get_row.return_value = None
|
|
registry.list_agents.return_value = []
|
|
registry.create = AsyncMock(return_value=object())
|
|
|
|
await bootstrap_default_agent(
|
|
registry,
|
|
catalog,
|
|
user_id=1,
|
|
agent_id=SETUP_DEFAULT_AGENT_ID,
|
|
)
|
|
|
|
spec = registry.create.await_args.args[0]
|
|
assert spec.config["backend"] == default_home_local_backend()
|
|
assert spec.config["backend"]["root_dir"] == host_fs_tree_root()
|
|
|
|
|
|
async def test_bootstrap_respects_policy_root_dir(catalog: ExpertCatalog, tmp_path: Path) -> None:
|
|
registry = MagicMock()
|
|
registry.get_row.return_value = None
|
|
registry.list_agents.return_value = []
|
|
registry.create = AsyncMock(return_value=object())
|
|
jail = (tmp_path / "jail").resolve().as_posix()
|
|
|
|
await bootstrap_default_agent(
|
|
registry,
|
|
catalog,
|
|
user_id=1,
|
|
agent_id=None,
|
|
root_dir=jail,
|
|
)
|
|
|
|
spec = registry.create.await_args.args[0]
|
|
assert spec.config["backend"] == default_home_local_backend(root_dir=jail)
|
|
assert spec.config["backend"]["root_dir"] == jail
|
|
|
|
|
|
async def test_bootstrap_requires_catalog() -> None:
|
|
registry = MagicMock()
|
|
registry.get_row.return_value = None
|
|
registry.list_agents.return_value = []
|
|
with pytest.raises(OctopError):
|
|
await bootstrap_default_agent(registry, None, user_id=1, locale="zh")
|