1
0
This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/tests/integrations/test_paperclip_bridge.py

206 lines
7.5 KiB
Python

"""Tests for the Paperclip bridge (CEO orchestration logic)."""
from unittest.mock import AsyncMock, patch, MagicMock
import pytest
from integrations.paperclip.bridge import PaperclipBridge
from integrations.paperclip.client import PaperclipClient
from integrations.paperclip.models import PaperclipAgent, PaperclipGoal, PaperclipIssue
@pytest.fixture
def mock_client():
client = MagicMock(spec=PaperclipClient)
# Make all methods async
client.healthy = AsyncMock(return_value=True)
client.list_agents = AsyncMock(return_value=[])
client.list_issues = AsyncMock(return_value=[])
client.list_goals = AsyncMock(return_value=[])
client.list_approvals = AsyncMock(return_value=[])
client.list_heartbeat_runs = AsyncMock(return_value=[])
client.get_issue = AsyncMock(return_value=None)
client.get_org = AsyncMock(return_value=None)
client.create_issue = AsyncMock(return_value=None)
client.update_issue = AsyncMock(return_value=None)
client.add_comment = AsyncMock(return_value=None)
client.wake_agent = AsyncMock(return_value=None)
client.create_goal = AsyncMock(return_value=None)
client.approve = AsyncMock(return_value=None)
client.reject = AsyncMock(return_value=None)
client.cancel_run = AsyncMock(return_value=None)
client.list_comments = AsyncMock(return_value=[])
return client
@pytest.fixture
def bridge(mock_client):
return PaperclipBridge(client=mock_client)
# ── status ───────────────────────────────────────────────────────────────────
async def test_status_when_disabled(bridge):
with patch("integrations.paperclip.bridge.settings") as mock_settings:
mock_settings.paperclip_enabled = False
mock_settings.paperclip_url = "http://localhost:3100"
status = await bridge.get_status()
assert status.enabled is False
async def test_status_when_connected(bridge, mock_client):
mock_client.healthy.return_value = True
mock_client.list_agents.return_value = [
PaperclipAgent(id="a1", name="Codex"),
]
mock_client.list_issues.return_value = [
PaperclipIssue(id="i1", title="Bug"),
PaperclipIssue(id="i2", title="Feature"),
]
with patch("integrations.paperclip.bridge.settings") as mock_settings:
mock_settings.paperclip_enabled = True
mock_settings.paperclip_url = "http://vps:3100"
mock_settings.paperclip_company_id = "comp-1"
status = await bridge.get_status()
assert status.enabled is True
assert status.connected is True
assert status.agent_count == 1
assert status.issue_count == 2
async def test_status_when_disconnected(bridge, mock_client):
mock_client.healthy.return_value = False
with patch("integrations.paperclip.bridge.settings") as mock_settings:
mock_settings.paperclip_enabled = True
mock_settings.paperclip_url = "http://vps:3100"
mock_settings.paperclip_company_id = "comp-1"
status = await bridge.get_status()
assert status.enabled is True
assert status.connected is False
assert "Cannot reach" in status.error
# ── create and assign ────────────────────────────────────────────────────────
async def test_create_and_assign_with_wake(bridge, mock_client):
issue = PaperclipIssue(id="i1", title="Deploy v2")
mock_client.create_issue.return_value = issue
mock_client.wake_agent.return_value = {"status": "queued"}
result = await bridge.create_and_assign(
title="Deploy v2",
assignee_id="agent-codex",
wake=True,
)
assert result is not None
assert result.id == "i1"
mock_client.wake_agent.assert_awaited_once_with("agent-codex", issue_id="i1")
async def test_create_and_assign_no_wake(bridge, mock_client):
issue = PaperclipIssue(id="i2", title="Research task")
mock_client.create_issue.return_value = issue
result = await bridge.create_and_assign(
title="Research task",
assignee_id="agent-research",
wake=False,
)
assert result is not None
mock_client.wake_agent.assert_not_awaited()
async def test_create_and_assign_failure(bridge, mock_client):
mock_client.create_issue.return_value = None
result = await bridge.create_and_assign(title="Will fail")
assert result is None
# ── delegate ─────────────────────────────────────────────────────────────────
async def test_delegate_issue(bridge, mock_client):
mock_client.update_issue.return_value = PaperclipIssue(id="i1", title="Task")
mock_client.wake_agent.return_value = {"status": "queued"}
ok = await bridge.delegate_issue("i1", "agent-codex", message="Handle this")
assert ok is True
mock_client.add_comment.assert_awaited_once()
mock_client.wake_agent.assert_awaited_once()
async def test_delegate_issue_update_fails(bridge, mock_client):
mock_client.update_issue.return_value = None
ok = await bridge.delegate_issue("i1", "agent-codex")
assert ok is False
# ── close issue ──────────────────────────────────────────────────────────────
async def test_close_issue(bridge, mock_client):
mock_client.update_issue.return_value = PaperclipIssue(id="i1", title="Done")
ok = await bridge.close_issue("i1", comment="Shipped!")
assert ok is True
mock_client.add_comment.assert_awaited_once()
# ── goals ────────────────────────────────────────────────────────────────────
async def test_set_goal(bridge, mock_client):
mock_client.create_goal.return_value = PaperclipGoal(id="g1", title="Ship MVP")
goal = await bridge.set_goal("Ship MVP")
assert goal is not None
assert goal.title == "Ship MVP"
# ── approvals ────────────────────────────────────────────────────────────────
async def test_approve(bridge, mock_client):
mock_client.approve.return_value = {"status": "approved"}
ok = await bridge.approve("ap1")
assert ok is True
async def test_reject(bridge, mock_client):
mock_client.reject.return_value = {"status": "rejected"}
ok = await bridge.reject("ap1", comment="Needs work")
assert ok is True
async def test_approve_failure(bridge, mock_client):
mock_client.approve.return_value = None
ok = await bridge.approve("ap1")
assert ok is False
# ── runs ─────────────────────────────────────────────────────────────────────
async def test_active_runs(bridge, mock_client):
mock_client.list_heartbeat_runs.return_value = [
{"id": "r1", "status": "running"},
]
runs = await bridge.active_runs()
assert len(runs) == 1
async def test_cancel_run(bridge, mock_client):
mock_client.cancel_run.return_value = {"status": "cancelled"}
ok = await bridge.cancel_run("r1")
assert ok is True