open-swe/tests/test_repo_extraction.py

188 lines
7.4 KiB
Python
Raw Normal View History

"""Tests for agent.utils.repo and Linear webhook repo override behavior."""
import json
from unittest.mock import AsyncMock, patch
import pytest
from agent.utils.repo import extract_repo_from_text
from agent.utils.slack import extract_channel_description_text
class TestExtractRepoFromText:
def test_repo_colon_with_org(self) -> None:
result = extract_repo_from_text("please use repo:my-org/my-repo")
assert result == {"owner": "my-org", "name": "my-repo"}
def test_repo_space_with_org(self) -> None:
result = extract_repo_from_text("please use repo langchain-ai/langchainjs")
assert result == {"owner": "langchain-ai", "name": "langchainjs"}
def test_repo_colon_name_only_uses_default_owner(self) -> None:
result = extract_repo_from_text("fix bug in repo:langchainplus")
assert result == {"owner": "langchain-ai", "name": "langchainplus"}
def test_repo_space_name_only_uses_default_owner(self) -> None:
result = extract_repo_from_text("fix bug in repo open-swe")
assert result == {"owner": "langchain-ai", "name": "open-swe"}
def test_repo_name_only_custom_default_owner(self) -> None:
result = extract_repo_from_text("repo:my-repo", default_owner="custom-org")
assert result == {"owner": "custom-org", "name": "my-repo"}
def test_github_url(self) -> None:
result = extract_repo_from_text(
"check https://github.com/langchain-ai/langgraph-api please"
)
assert result == {"owner": "langchain-ai", "name": "langgraph-api"}
def test_explicit_repo_beats_github_url(self) -> None:
result = extract_repo_from_text(
"see https://github.com/langchain-ai/langgraph-api but use repo:my-org/my-repo"
)
assert result == {"owner": "my-org", "name": "my-repo"}
def test_no_repo_returns_none(self) -> None:
result = extract_repo_from_text("please fix the bug")
assert result is None
def test_empty_string_returns_none(self) -> None:
result = extract_repo_from_text("")
assert result is None
def test_trailing_slash_stripped(self) -> None:
result = extract_repo_from_text("repo:my-org/my-repo/")
assert result == {"owner": "my-org", "name": "my-repo"}
class TestExtractChannelDescriptionText:
def test_combines_topic_and_purpose(self) -> None:
channel = {
"topic": {"value": "repo:my-org/my-repo"},
"purpose": {"value": "Team channel"},
}
assert extract_channel_description_text(channel) == "repo:my-org/my-repo\nTeam channel"
def test_handles_missing_sections(self) -> None:
assert extract_channel_description_text({"topic": {"value": "hi"}}) == "hi"
def test_empty_for_none(self) -> None:
assert extract_channel_description_text(None) == ""
def test_empty_for_blank_values(self) -> None:
channel = {"topic": {"value": " "}, "purpose": {"value": ""}}
assert extract_channel_description_text(channel) == ""
def test_repo_token_extractable_from_description(self) -> None:
channel = {"topic": {"value": "Use repo:langchain-ai/open-swe here"}, "purpose": {}}
description = extract_channel_description_text(channel)
assert extract_repo_from_text(description) == {
"owner": "langchain-ai",
"name": "open-swe",
}
class TestLinearWebhookRepoOverride:
"""Test that the Linear webhook handler checks comment body for repo config first."""
@pytest.fixture()
def _base_payload(self) -> dict:
return {
"type": "Comment",
"action": "create",
"data": {
"id": "comment-123",
"body": "@openswe please fix this repo:custom-org/custom-repo",
"issue": {
"id": "issue-456",
"title": "Test issue",
},
"user": {"id": "user-1", "name": "Test User", "email": "test@test.com"},
},
}
@pytest.mark.asyncio
async def test_comment_repo_overrides_team_mapping(self, _base_payload: dict) -> None:
refactor: split webapp.py into api/ + per-source webhook routes 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.
2026-07-17 14:30:05 -04:00
from agent.webhooks.linear_routes import linear_webhook
with (
refactor: split webapp.py into api/ + per-source webhook routes 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.
2026-07-17 14:30:05 -04:00
patch("agent.webhooks.common.verify_linear_signature", return_value=True),
patch(
refactor: split webapp.py into api/ + per-source webhook routes 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.
2026-07-17 14:30:05 -04:00
"agent.webhooks.common.fetch_linear_issue_details",
new_callable=AsyncMock,
return_value={
"id": "issue-456",
"title": "Test issue",
"identifier": "TEST-1",
"url": "https://linear.app/test/issue/TEST-1",
"team": {"id": "t1", "name": "Some Team", "key": "ST"},
"project": {"id": "p1", "name": "Some Project"},
"comments": {"nodes": []},
},
),
refactor: split webapp.py into api/ + per-source webhook routes 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.
2026-07-17 14:30:05 -04:00
patch("agent.webhooks.common._is_repo_allowed", return_value=True),
patch("agent.webhooks.common.BackgroundTasks"),
):
mock_request = AsyncMock()
mock_request.body.return_value = json.dumps(_base_payload).encode()
mock_request.headers = {"Linear-Signature": "valid"}
bg_tasks = AsyncMock()
result = await linear_webhook(mock_request, bg_tasks)
assert result["status"] == "accepted"
assert "custom-org/custom-repo" in result["message"]
call_args = bg_tasks.add_task.call_args
repo_config = call_args[0][2]
assert repo_config == {"owner": "custom-org", "name": "custom-repo"}
@pytest.mark.asyncio
async def test_falls_back_to_team_mapping_when_no_repo_in_comment(self) -> None:
refactor: split webapp.py into api/ + per-source webhook routes 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.
2026-07-17 14:30:05 -04:00
from agent.webhooks.linear_routes import linear_webhook
payload = {
"type": "Comment",
"action": "create",
"data": {
"id": "comment-123",
"body": "@openswe please fix this bug",
"issue": {
"id": "issue-456",
"title": "Test issue",
},
"user": {"id": "user-1", "name": "Test User", "email": "test@test.com"},
},
}
with (
refactor: split webapp.py into api/ + per-source webhook routes 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.
2026-07-17 14:30:05 -04:00
patch("agent.webhooks.common.verify_linear_signature", return_value=True),
patch(
refactor: split webapp.py into api/ + per-source webhook routes 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.
2026-07-17 14:30:05 -04:00
"agent.webhooks.common.fetch_linear_issue_details",
new_callable=AsyncMock,
return_value={
"id": "issue-456",
"title": "Test issue",
"identifier": "TEST-1",
"url": "https://linear.app/test/issue/TEST-1",
"team": {"id": "t1", "name": "Open SWE", "key": "OS"},
"project": None,
"comments": {"nodes": []},
},
),
refactor: split webapp.py into api/ + per-source webhook routes 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.
2026-07-17 14:30:05 -04:00
patch("agent.webhooks.common._is_repo_allowed", return_value=True),
):
mock_request = AsyncMock()
mock_request.body.return_value = json.dumps(payload).encode()
mock_request.headers = {"Linear-Signature": "valid"}
bg_tasks = AsyncMock()
result = await linear_webhook(mock_request, bg_tasks)
assert result["status"] == "accepted"
assert "langchain-ai/open-swe" in result["message"]
call_args = bg_tasks.add_task.call_args
repo_config = call_args[0][2]
assert repo_config == {"owner": "langchain-ai", "name": "open-swe"}