a2514101af
Adds 156 tests across 9 new test files covering: - coordinator_mode: TaskNotification XML, is_coordinator_mode, WorkerConfig - agent_definitions: AgentDefinition model, built-ins, load_agents_dir - swarm/types: TeammateIdentity, SpawnResult, TeammateExecutor protocol - swarm/registry: BackendRegistry register/detect/get_executor - swarm/mailbox: TeammateMailbox write/read/mark_read/clear + factories - swarm/in_process: InProcessBackend spawn/shutdown/send_message, contextvars - swarm/permission_sync: create/send/poll/handle permission flow - swarm/team_lifecycle: TeamLifecycleManager CRUD with tmp_path fixtures - swarm/worktree: validate_worktree_slug edge cases, flatten/branch helpers
198 lines
5.8 KiB
Python
198 lines
5.8 KiB
Python
"""Tests for TeamLifecycleManager CRUD operations with tmp_path fixtures."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from openharness.swarm.team_lifecycle import (
|
|
TeamFile,
|
|
TeamLifecycleManager,
|
|
TeamMember,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fixtures
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.fixture
|
|
def manager(tmp_path, monkeypatch):
|
|
"""Return a TeamLifecycleManager whose teams live inside tmp_path."""
|
|
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
|
return TeamLifecycleManager()
|
|
|
|
|
|
def _make_member(agent_id: str = "worker1@alpha", name: str = "worker1") -> TeamMember:
|
|
return TeamMember(
|
|
agent_id=agent_id,
|
|
name=name,
|
|
backend_type="subprocess",
|
|
joined_at=time.time(),
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TeamMember serialization
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_team_member_round_trip():
|
|
member = _make_member()
|
|
data = member.to_dict()
|
|
restored = TeamMember.from_dict(data)
|
|
assert restored.agent_id == member.agent_id
|
|
assert restored.name == member.name
|
|
assert restored.backend_type == member.backend_type
|
|
assert restored.status == "active"
|
|
|
|
|
|
def test_team_member_default_status():
|
|
member = _make_member()
|
|
assert member.status == "active"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TeamFile serialization
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_team_file_round_trip(tmp_path):
|
|
tf = TeamFile(name="myteam", created_at=time.time(), description="test team")
|
|
path = tmp_path / "team.json"
|
|
tf.save(path)
|
|
loaded = TeamFile.load(path)
|
|
assert loaded.name == "myteam"
|
|
assert loaded.description == "test team"
|
|
|
|
|
|
def test_team_file_load_missing_raises(tmp_path):
|
|
with pytest.raises(FileNotFoundError):
|
|
TeamFile.load(tmp_path / "nonexistent.json")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TeamLifecycleManager.create_team
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_create_team_persists_to_disk(manager):
|
|
tf = manager.create_team("alpha", "first team")
|
|
assert tf.name == "alpha"
|
|
assert tf.description == "first team"
|
|
|
|
# Verify it was written to disk
|
|
reloaded = manager.get_team("alpha")
|
|
assert reloaded is not None
|
|
assert reloaded.name == "alpha"
|
|
|
|
|
|
def test_create_team_duplicate_raises(manager):
|
|
manager.create_team("beta")
|
|
with pytest.raises(ValueError, match="already exists"):
|
|
manager.create_team("beta")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TeamLifecycleManager.get_team
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_get_team_returns_none_for_missing(manager):
|
|
result = manager.get_team("no-such-team")
|
|
assert result is None
|
|
|
|
|
|
def test_get_team_returns_team_file(manager):
|
|
manager.create_team("gamma")
|
|
tf = manager.get_team("gamma")
|
|
assert tf is not None
|
|
assert tf.name == "gamma"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TeamLifecycleManager.delete_team
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_delete_team_removes_from_disk(manager):
|
|
manager.create_team("to-delete")
|
|
manager.delete_team("to-delete")
|
|
assert manager.get_team("to-delete") is None
|
|
|
|
|
|
def test_delete_nonexistent_team_raises(manager):
|
|
with pytest.raises(ValueError, match="does not exist"):
|
|
manager.delete_team("ghost")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TeamLifecycleManager.list_teams
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_list_teams_empty_initially(manager):
|
|
teams = manager.list_teams()
|
|
assert teams == []
|
|
|
|
|
|
def test_list_teams_returns_all_sorted(manager):
|
|
for name in ("charlie", "alpha", "bravo"):
|
|
manager.create_team(name)
|
|
teams = manager.list_teams()
|
|
names = [t.name for t in teams]
|
|
assert names == sorted(names)
|
|
assert set(names) == {"alpha", "bravo", "charlie"}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TeamLifecycleManager.add_member / remove_member
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_add_member_persists(manager):
|
|
manager.create_team("delta")
|
|
member = _make_member()
|
|
updated = manager.add_member("delta", member)
|
|
assert member.agent_id in updated.members
|
|
|
|
reloaded = manager.get_team("delta")
|
|
assert reloaded is not None
|
|
assert member.agent_id in reloaded.members
|
|
|
|
|
|
def test_add_member_replaces_existing(manager):
|
|
manager.create_team("epsilon")
|
|
m1 = TeamMember(
|
|
agent_id="w@epsilon", name="old", backend_type="subprocess", joined_at=1.0
|
|
)
|
|
manager.add_member("epsilon", m1)
|
|
|
|
m2 = TeamMember(
|
|
agent_id="w@epsilon", name="new", backend_type="in_process", joined_at=2.0
|
|
)
|
|
updated = manager.add_member("epsilon", m2)
|
|
assert updated.members["w@epsilon"].name == "new"
|
|
|
|
|
|
def test_remove_member(manager):
|
|
manager.create_team("zeta")
|
|
member = _make_member("x@zeta", "x")
|
|
manager.add_member("zeta", member)
|
|
updated = manager.remove_member("zeta", "x@zeta")
|
|
assert "x@zeta" not in updated.members
|
|
|
|
|
|
def test_remove_nonexistent_member_raises(manager):
|
|
manager.create_team("eta")
|
|
with pytest.raises(ValueError, match="not a member"):
|
|
manager.remove_member("eta", "ghost@eta")
|
|
|
|
|
|
def test_add_member_to_nonexistent_team_raises(manager):
|
|
with pytest.raises(ValueError, match="does not exist"):
|
|
manager.add_member("no-team", _make_member())
|