feat(boards): add max_agents field to board models and enforce limits
This commit is contained in:
68
backend/tests/test_agent_create_limits.py
Normal file
68
backend/tests/test_agent_create_limits.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# ruff: noqa: S101
|
||||
"""Unit tests for board worker-agent spawn limits."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from types import SimpleNamespace
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
import app.services.openclaw.provisioning_db as agent_service
|
||||
from app.schemas.agents import AgentCreate
|
||||
|
||||
|
||||
@dataclass
|
||||
class _FakeSession:
|
||||
async def exec(self, *_args: object, **_kwargs: object) -> None:
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
class _BoardStub:
|
||||
id: UUID
|
||||
gateway_id: UUID
|
||||
max_agents: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class _AgentStub:
|
||||
id: UUID
|
||||
board_id: UUID | None
|
||||
is_board_lead: bool
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_agent_as_lead_enforces_board_max_agents(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
service = agent_service.AgentLifecycleService(_FakeSession()) # type: ignore[arg-type]
|
||||
|
||||
board_id = uuid4()
|
||||
board = _BoardStub(id=board_id, gateway_id=uuid4(), max_agents=1)
|
||||
lead = _AgentStub(id=uuid4(), board_id=board_id, is_board_lead=True)
|
||||
actor = SimpleNamespace(actor_type="agent", user=None, agent=lead)
|
||||
payload = AgentCreate(name="Worker Agent", board_id=board_id)
|
||||
|
||||
async def _fake_require_board(*_args: object, **_kwargs: object) -> _BoardStub:
|
||||
return board
|
||||
|
||||
async def _fake_count_non_lead_agents_for_board(*, board_id: UUID) -> int:
|
||||
assert board_id == board.id
|
||||
return 1
|
||||
|
||||
monkeypatch.setattr(service, "require_board", _fake_require_board)
|
||||
monkeypatch.setattr(
|
||||
service,
|
||||
"count_non_lead_agents_for_board",
|
||||
_fake_count_non_lead_agents_for_board,
|
||||
)
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service.create_agent(payload=payload, actor=actor) # type: ignore[arg-type]
|
||||
|
||||
assert exc_info.value.status_code == status.HTTP_409_CONFLICT
|
||||
assert "excluding the lead" in str(exc_info.value.detail)
|
||||
assert "max_agents=1" in str(exc_info.value.detail)
|
||||
@@ -88,17 +88,35 @@ def test_board_rule_toggles_have_expected_defaults() -> None:
|
||||
assert created.require_review_before_done is False
|
||||
assert created.block_status_changes_with_pending_approval is False
|
||||
assert created.only_lead_can_change_status is False
|
||||
assert created.max_agents == 1
|
||||
|
||||
updated = BoardUpdate(
|
||||
require_approval_for_done=False,
|
||||
require_review_before_done=True,
|
||||
block_status_changes_with_pending_approval=True,
|
||||
only_lead_can_change_status=True,
|
||||
max_agents=3,
|
||||
)
|
||||
assert updated.require_approval_for_done is False
|
||||
assert updated.require_review_before_done is True
|
||||
assert updated.block_status_changes_with_pending_approval is True
|
||||
assert updated.only_lead_can_change_status is True
|
||||
assert updated.max_agents == 3
|
||||
|
||||
|
||||
def test_board_max_agents_must_be_non_negative() -> None:
|
||||
"""Board max_agents should reject negative values."""
|
||||
with pytest.raises(ValueError):
|
||||
BoardCreate(
|
||||
name="Ops Board",
|
||||
slug="ops-board",
|
||||
description="Operations workflow board.",
|
||||
gateway_id=uuid4(),
|
||||
max_agents=-1,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
BoardUpdate(max_agents=-1)
|
||||
|
||||
|
||||
def test_onboarding_confirm_requires_goal_fields() -> None:
|
||||
|
||||
Reference in New Issue
Block a user