| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- from __future__ import annotations
- import json
- import os
- import stat
- from dataclasses import replace
- from pathlib import Path
- import pytest
- from agency_swarm.integrations import openclaw as openclaw_mod
- from agency_swarm.integrations.openclaw import OpenClawRuntime
- from tests.integration.fastapi._openclaw_test_support import _build_openclaw_config
- def test_openclaw_ensure_layout_creates_config_parent_dir(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- custom_config_path = tmp_path / "external" / "nested" / "openclaw.json"
- runtime = OpenClawRuntime(replace(config, config_path=custom_config_path))
- runtime.ensure_layout()
- assert custom_config_path.exists()
- assert custom_config_path.parent.is_dir()
- if os.name != "nt":
- assert stat.S_IMODE(custom_config_path.stat().st_mode) == 0o600
- def test_openclaw_ensure_layout_writes_config_with_secure_create_mode(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
- ) -> None:
- config = _build_openclaw_config(tmp_path)
- runtime = OpenClawRuntime(config)
- original_open = openclaw_mod.os.open
- seen_modes: list[int] = []
- def _open(path: os.PathLike[str] | str, flags: int, mode: int = 0o777) -> int:
- seen_modes.append(mode)
- return original_open(path, flags, mode)
- monkeypatch.setattr(openclaw_mod.os, "open", _open)
- runtime.ensure_layout()
- assert seen_modes
- assert seen_modes[-1] == 0o600
- def test_openclaw_ensure_layout_defaults_workspace_to_home_workspace(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- runtime = OpenClawRuntime(config)
- runtime.ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(config.workspace_dir)
- assert config.workspace_dir.is_dir()
- def test_openclaw_ensure_layout_keeps_existing_workspace_override(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- custom_workspace = tmp_path / "custom-workspace"
- config.config_path.parent.mkdir(parents=True, exist_ok=True)
- config.config_path.write_text(
- json.dumps({"agents": {"defaults": {"workspace": str(custom_workspace)}}}),
- encoding="utf-8",
- )
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(custom_workspace)
- assert not config.workspace_dir.exists()
- def test_openclaw_ensure_layout_replaces_blank_workspace_override(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- config.config_path.parent.mkdir(parents=True, exist_ok=True)
- config.config_path.write_text(
- json.dumps({"agents": {"defaults": {"workspace": " "}}}),
- encoding="utf-8",
- )
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(config.workspace_dir)
- assert config.workspace_dir.is_dir()
- def test_openclaw_ensure_layout_migrates_legacy_workspace_when_default_is_missing(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- legacy_workspace = config.legacy_workspace_dir
- legacy_workspace.mkdir(parents=True, exist_ok=True)
- (legacy_workspace / "AGENTS.md").write_text("legacy", encoding="utf-8")
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(config.workspace_dir)
- assert (config.workspace_dir / "AGENTS.md").read_text(encoding="utf-8") == "legacy"
- assert not legacy_workspace.exists()
- def test_openclaw_ensure_layout_preserves_legacy_workspace_when_new_and_old_both_exist(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- legacy_workspace = config.legacy_workspace_dir
- legacy_workspace.mkdir(parents=True, exist_ok=True)
- (legacy_workspace / "AGENTS.md").write_text("legacy", encoding="utf-8")
- config.workspace_dir.mkdir(parents=True, exist_ok=True)
- (config.workspace_dir / "AGENTS.md").write_text("new", encoding="utf-8")
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(legacy_workspace)
- assert (legacy_workspace / "AGENTS.md").read_text(encoding="utf-8") == "legacy"
- assert (config.workspace_dir / "AGENTS.md").read_text(encoding="utf-8") == "new"
- def test_openclaw_ensure_layout_merges_legacy_workspace_into_empty_new_dir(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- legacy_workspace = config.legacy_workspace_dir
- legacy_workspace.mkdir(parents=True, exist_ok=True)
- (legacy_workspace / "AGENTS.md").write_text("legacy", encoding="utf-8")
- config.workspace_dir.mkdir(parents=True, exist_ok=True)
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(config.workspace_dir)
- assert (config.workspace_dir / "AGENTS.md").read_text(encoding="utf-8") == "legacy"
- assert not legacy_workspace.exists()
- def test_openclaw_ensure_layout_keeps_explicit_agent_workspace_override(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- legacy_workspace = config.legacy_workspace_dir
- legacy_workspace.mkdir(parents=True, exist_ok=True)
- (legacy_workspace / "AGENTS.md").write_text("legacy", encoding="utf-8")
- config.config_path.parent.mkdir(parents=True, exist_ok=True)
- config.config_path.write_text(
- json.dumps({"agents": {"list": [{"id": "main", "workspace": str(legacy_workspace)}]}}),
- encoding="utf-8",
- )
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["list"][0]["workspace"] == str(legacy_workspace)
- assert (legacy_workspace / "AGENTS.md").read_text(encoding="utf-8") == "legacy"
- assert not config.workspace_dir.exists()
- def test_openclaw_ensure_layout_keeps_default_workspace_migration_when_other_agent_has_override(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- config.config_path.parent.mkdir(parents=True, exist_ok=True)
- specialist_workspace = tmp_path / "specialist-workspace"
- config.config_path.write_text(
- json.dumps(
- {
- "agents": {
- "list": [
- {"id": "main", "default": True},
- {"id": "specialist", "workspace": str(specialist_workspace)},
- ]
- }
- }
- ),
- encoding="utf-8",
- )
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(config.workspace_dir)
- assert payload["agents"]["list"][1]["workspace"] == str(specialist_workspace)
- def test_openclaw_ensure_layout_uses_profile_aware_workspace_suffix(tmp_path: Path) -> None:
- config = replace(_build_openclaw_config(tmp_path), profile="team-a")
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(config.home_dir / "workspace-team-a")
- assert (config.home_dir / "workspace-team-a").is_dir()
- def test_openclaw_ensure_layout_uses_env_profile_for_manual_config(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
- ) -> None:
- monkeypatch.setenv("OPENCLAW_PROFILE", "team-a")
- config = _build_openclaw_config(tmp_path)
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(config.home_dir / "workspace-team-a")
- def test_openclaw_ensure_layout_treats_default_profile_as_unsuffixed_workspace(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
- ) -> None:
- monkeypatch.setenv("OPENCLAW_PROFILE", "default")
- config = _build_openclaw_config(tmp_path)
- OpenClawRuntime(config).ensure_layout()
- payload = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert payload["agents"]["defaults"]["workspace"] == str(config.home_dir / "workspace")
- def test_openclaw_ensure_layout_fails_cleanly_when_workspace_path_is_a_file(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- config.workspace_dir.parent.mkdir(parents=True, exist_ok=True)
- config.workspace_dir.write_text("not-a-dir", encoding="utf-8")
- with pytest.raises(RuntimeError, match="workspace path collision"):
- OpenClawRuntime(config).ensure_layout()
- def test_openclaw_ensure_layout_normalizes_existing_non_dict_config_sections(tmp_path: Path) -> None:
- config = _build_openclaw_config(tmp_path)
- config.config_path.parent.mkdir(parents=True, exist_ok=True)
- config.config_path.write_text(
- json.dumps(
- {
- "gateway": [],
- "agents": {"defaults": []},
- }
- ),
- encoding="utf-8",
- )
- runtime = OpenClawRuntime(config)
- runtime.ensure_layout()
- saved = json.loads(config.config_path.read_text(encoding="utf-8"))
- assert saved["gateway"]["auth"]["mode"] == "token"
- assert saved["gateway"]["http"]["endpoints"]["responses"]["enabled"] is True
- assert saved["agents"]["defaults"]["model"] == {"primary": "openai/gpt-5.4-mini"}
- assert saved["agents"]["defaults"]["workspace"] == str(config.workspace_dir)
|