mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 08:03:15 +00:00
Plan step C4 (docs/upstream-sync/domain-reorg/reorg-build-plan.md, approved
decisions 1-2): split the 2,590-line agent/webapp.py monolith into
agent/webhooks/common.py (shared verify/dispatch helpers), agent/api/app.py
(composition), agent/api/health.py (/health + /webhooks/run-complete), and
per-source {github,linear,slack,jira,confluence}_routes.py. Atlassian
Connect lifecycle + descriptor routes (/connect/*) fold into
confluence_routes.py; webapp.py becomes the upstream-shaped compatibility
shim (from .api.app import app). langgraph.json http.app stays
agent.webapp:app via the shim.
Fork content, upstream layout: linear/slack route files verified
content-identical to upstream 8356eb34 and taken verbatim; github_routes is
upstream + the fork's CI auto-fix trigger wiring; jira/confluence routes are
fork-only, transformed to the same common.X / service.X module-attribute
style. All signature verification (GitHub HMAC, Slack, Linear
timestamp-freshness, verify_jira_secret + opt-in HMAC/timestamp/IP
allowlist, Connect JWT/qsh), token-attribution gating, TID-COLLIDE-01 repo
binding, _is_repo_auto_review_enabled gates, and public-repo org gate move
unchanged.
Handlers rewired from webapp.X to common.X; test monkeypatch sites across
26 files + conftest.py + e2e/harness.py retargeted to
webhook_common/handler/route modules per upstream's pattern. Residual
agent.webapp importers: only the shim, langgraph.json http.app, Makefile
uvicorn target, and docs (doc-path updates land in C7).
Gates: ruff check + format, pytest --co, full unit (1637 passed), full
Playwright E2E vs real langgraph dev (9/9), residual-importer sweep.
93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
"""Unit tests for agent-thread PR-state tracking from PR webhook events."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from agent.webhooks import common as webhook_common
|
|
|
|
|
|
def _pr_payload(*, state: str, merged: bool = False, draft: bool = False) -> dict[str, Any]:
|
|
return {
|
|
"pull_request": {
|
|
"html_url": "https://github.com/lc/repo/pull/7",
|
|
"state": state,
|
|
"merged": merged,
|
|
"draft": draft,
|
|
}
|
|
}
|
|
|
|
|
|
def test_pr_state_from_payload_merged() -> None:
|
|
assert (
|
|
webhook_common._pr_state_from_payload(_pr_payload(state="closed", merged=True)) == "merged"
|
|
)
|
|
|
|
|
|
def test_pr_state_from_payload_closed() -> None:
|
|
assert webhook_common._pr_state_from_payload(_pr_payload(state="closed")) == "closed"
|
|
|
|
|
|
def test_pr_state_from_payload_draft() -> None:
|
|
assert webhook_common._pr_state_from_payload(_pr_payload(state="open", draft=True)) == "draft"
|
|
|
|
|
|
def test_pr_state_from_payload_open() -> None:
|
|
assert webhook_common._pr_state_from_payload(_pr_payload(state="open")) == "open"
|
|
|
|
|
|
def test_pr_state_from_payload_missing_pull_request() -> None:
|
|
assert webhook_common._pr_state_from_payload({}) is None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_agent_thread_pr_state_updates_matching_thread() -> None:
|
|
fake_client = MagicMock()
|
|
fake_client.threads.search = AsyncMock(
|
|
return_value=[
|
|
{
|
|
"thread_id": "t1",
|
|
"metadata": {"kind": "agent", "pr_state": "draft"},
|
|
}
|
|
]
|
|
)
|
|
fake_client.threads.update = AsyncMock()
|
|
|
|
with patch("agent.webhooks.common.get_client", return_value=fake_client):
|
|
await webhook_common.update_agent_thread_pr_state(_pr_payload(state="closed"))
|
|
|
|
fake_client.threads.search.assert_awaited_once()
|
|
fake_client.threads.update.assert_awaited_once()
|
|
assert fake_client.threads.update.await_args.kwargs["thread_id"] == "t1"
|
|
assert fake_client.threads.update.await_args.kwargs["metadata"] == {"pr_state": "closed"}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_agent_thread_pr_state_skips_reviewer_threads() -> None:
|
|
fake_client = MagicMock()
|
|
fake_client.threads.search = AsyncMock(
|
|
return_value=[{"thread_id": "rev", "metadata": {"kind": "reviewer"}}]
|
|
)
|
|
fake_client.threads.update = AsyncMock()
|
|
|
|
with patch("agent.webhooks.common.get_client", return_value=fake_client):
|
|
await webhook_common.update_agent_thread_pr_state(_pr_payload(state="closed"))
|
|
|
|
fake_client.threads.update.assert_not_called()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_agent_thread_pr_state_noop_when_state_unchanged() -> None:
|
|
fake_client = MagicMock()
|
|
fake_client.threads.search = AsyncMock(
|
|
return_value=[{"thread_id": "t1", "metadata": {"pr_state": "merged"}}]
|
|
)
|
|
fake_client.threads.update = AsyncMock()
|
|
|
|
with patch("agent.webhooks.common.get_client", return_value=fake_client):
|
|
await webhook_common.update_agent_thread_pr_state(_pr_payload(state="closed", merged=True))
|
|
|
|
fake_client.threads.update.assert_not_called()
|