diff --git a/agent/utils/github_comments.py b/agent/utils/github_comments.py index 16723895..ce57d710 100644 --- a/agent/utils/github_comments.py +++ b/agent/utils/github_comments.py @@ -25,7 +25,6 @@ __all__ = [ "fetch_pr_comments_since_last_tag", "format_github_comment_body_for_prompt", "get_thread_id_from_branch", - "parse_github_review_command", "post_github_comment", "react_to_github_comment", "sanitize_github_comment_body", @@ -33,8 +32,6 @@ __all__ = [ ] OPEN_SWE_TAGS = ("@openswe", "@open-swe", "@openswe-dev") -_OPEN_SWE_MENTION_RE = re.compile(r"(?i)@(?:openswe-dev|open-swe|openswe)\b") -_REVIEW_COMMAND_RE = re.compile(r"(?i)\Areview(?:\s+(https?://\S+))?\s*\Z") UNTRUSTED_GITHUB_COMMENT_OPEN_TAG = "" UNTRUSTED_GITHUB_COMMENT_CLOSE_TAG = "" _SANITIZED_UNTRUSTED_GITHUB_COMMENT_OPEN_TAG = "[blocked-untrusted-comment-tag-open]" @@ -76,23 +73,6 @@ def get_thread_id_from_branch(branch_name: str) -> str | None: return match.group(0) if match else None -def parse_github_review_command(body: str) -> tuple[bool, str | None]: - """Detect ``@open-swe review [URL]`` in a GitHub comment body. - - Returns ``(is_review_command, optional_pr_url)``. When ``is_review_command`` - is True and the URL is None, the command applies to the PR being commented - on. The URL — if present — is returned as-is so the caller can validate it - with ``parse_github_pr_url``. - """ - if not body: - return False, None - stripped = _OPEN_SWE_MENTION_RE.sub("", body).strip() - match = _REVIEW_COMMAND_RE.match(stripped) - if not match: - return False, None - return True, match.group(1) or None - - def sanitize_github_comment_body(body: str) -> str: """Strip reserved trust wrapper tags from raw GitHub comment bodies.""" sanitized = body.replace( diff --git a/agent/utils/slack.py b/agent/utils/slack.py index 3db5de8c..b1d2d109 100644 --- a/agent/utils/slack.py +++ b/agent/utils/slack.py @@ -23,10 +23,6 @@ logger = logging.getLogger(__name__) SLACK_API_BASE_URL = "https://slack.com/api" SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN", "") -GITHUB_PR_URL_RE = re.compile(r"https?://(?:www\.)?github\.com/[^\s<>|]+/[^\s<>|]+/pull/\d+") -URL_RE = re.compile(r"https?://[^\s<>|]+") - - DEFAULT_ASSISTANT_STATUS = "is thinking…" # Curated rotating loading strings shown by Slack while the indicator is active. @@ -174,36 +170,6 @@ def parse_github_pr_url(url: str) -> GitHubPrRef | None: ) -def parse_slack_review_command(text: str) -> GitHubPrRef | None: - stripped = text.strip() - command_match = re.fullmatch(r"(?is)review\s+(.+)", stripped) - if not command_match: - return None - - rest = command_match.group(1).strip() - url_match = GITHUB_PR_URL_RE.search(rest) - if not url_match: - return None - - trailing_text = rest[url_match.end() :].strip() - if trailing_text and trailing_text != ">" and not trailing_text.startswith("|"): - return None - - return parse_github_pr_url(url_match.group(0)) - - -def looks_like_slack_pr_review_command(text: str) -> bool: - stripped = text.strip() - if not re.match(r"(?is)^review\b", stripped): - return False - for match in URL_RE.finditer(stripped): - parsed = urlparse(match.group(0).strip("<>")) - host = (parsed.hostname or "").lower() - if parsed.scheme in {"http", "https"} and host in {"github.com", "www.github.com"}: - return True - return False - - def select_slack_context_messages( messages: list[dict[str, Any]], current_message_ts: str, diff --git a/agent/webapp.py b/agent/webapp.py index 174a36e8..85de78d5 100644 --- a/agent/webapp.py +++ b/agent/webapp.py @@ -55,7 +55,6 @@ from .utils.auth import ( persist_encrypted_github_token, resolve_github_token_from_email, ) -from .utils.authorship import OPEN_SWE_BOT_NAME from .utils.comments import get_recent_comments from .utils.github_app import ( get_github_app_installation_token, @@ -70,7 +69,6 @@ from .utils.github_comments import ( fetch_pr_comments_since_last_tag, format_github_comment_body_for_prompt, get_thread_id_from_branch, - parse_github_review_command, react_to_github_comment, sanitize_github_comment_body, verify_github_signature, @@ -88,7 +86,6 @@ from .utils.slack import ( format_slack_messages_for_prompt, get_slack_user_info, get_slack_user_names, - parse_github_pr_url, post_slack_thread_reply, post_slack_trace_reply, resolve_slack_links_in_context, @@ -1163,33 +1160,6 @@ async def process_slack_mention(event_data: dict[str, Any], repo_config: dict[st ) -async def process_slack_pr_review_request( - pr_ref: GitHubPrRef, channel_id: str, thread_ts: str -) -> None: - await set_slack_assistant_status(channel_id, thread_ts) - result = await trigger_pr_review_from_ref( - pr_ref, - source="slack", - slack_channel_id=channel_id, - slack_thread_ts=thread_ts, - ) - if result.get("success"): - thread_id = result.get("thread_id") - if isinstance(thread_id, str) and thread_id: - await post_slack_trace_reply( - channel_id, thread_ts, thread_id, include_dashboard_link=False - ) - await set_slack_assistant_status(channel_id, thread_ts) - return - - await post_slack_thread_reply( - channel_id, - thread_ts, - f"Could not start review for <{pr_ref.url}|{pr_ref.owner}/{pr_ref.repo}#{pr_ref.number}>: " - f"{result.get('error', 'unknown error')}.", - ) - - def verify_linear_signature(body: bytes, signature: str, secret: str) -> bool: """Verify the Linear webhook signature. @@ -1494,7 +1464,6 @@ _SUPPORTED_GH_EVENTS = frozenset( _SUPPORTED_GH_ISSUE_ACTIONS = frozenset(["edited", "opened", "reopened"]) _SUPPORTED_GH_PULL_REQUEST_ACTIONS = frozenset( [ - "review_requested", "opened", "ready_for_review", "converted_to_draft", @@ -1619,12 +1588,6 @@ async def _trigger_or_queue_run( logger.info("LangGraph run created for thread %s from GitHub PR comment", thread_id) -def _is_open_swe_reviewer_request(payload: dict[str, Any]) -> bool: - reviewer = payload.get("requested_reviewer") or {} - login = reviewer.get("login", "") if isinstance(reviewer, dict) else "" - return login.lower() == OPEN_SWE_BOT_NAME.lower() - - def build_github_pr_review_prompt( repo_config: dict[str, str], pr_number: int, @@ -2005,11 +1968,6 @@ async def _dispatch_first_review_from_pr_payload(payload: dict[str, Any], *, sou logger.info("Reviewer run created for thread %s (source=%s)", thread_id, source) -async def process_github_pr_review_request(payload: dict[str, Any]) -> None: - """Trigger the reviewer agent when the Open SWE bot is requested on a PR.""" - await _dispatch_first_review_from_pr_payload(payload, source="github") - - async def process_github_pr_ready(payload: dict[str, Any]) -> None: """Auto-review a PR that has just been opened or marked ready-for-review. @@ -2033,75 +1991,6 @@ async def process_github_pr_ready(payload: dict[str, Any]) -> None: await _dispatch_first_review_from_pr_payload(payload, source="github") -async def process_github_pr_review_command( - payload: dict[str, Any], - event_type: str, - pr_url_override: str | None, -) -> None: - """Trigger the reviewer when a PR comment contains ``@open-swe review``. - - ``pr_url_override`` is the optional URL token that followed ``review``. If - set, the review targets that PR; otherwise the comment's own PR is used. - """ - repo = payload.get("repository", {}) - repo_config = { - "owner": repo.get("owner", {}).get("login", ""), - "name": repo.get("name", ""), - } - pr_data = payload.get("pull_request") or payload.get("issue", {}) - sender = payload.get("sender", {}) - github_login = sender.get("login", "") - github_user_id = sender.get("id") - - pr_ref: GitHubPrRef | None = None - if pr_url_override: - pr_ref = parse_github_pr_url(pr_url_override) - if pr_ref is None: - logger.info("Ignoring @open-swe review with unparseable URL %s", pr_url_override) - return - else: - pr_number = pr_data.get("number") - if not pr_number: - logger.warning("@open-swe review command missing pr_number, skipping") - return - pr_ref = GitHubPrRef( - owner=repo_config["owner"], - repo=repo_config["name"], - number=pr_number, - url=pr_data.get("html_url", "") or pr_data.get("url", ""), - ) - - comment = payload.get("comment") or payload.get("review", {}) - comment_id = comment.get("id") - node_id = comment.get("node_id") if event_type == "pull_request_review" else None - if comment_id: - app_token = await get_github_app_installation_token() - if app_token: - await react_to_github_comment( - repo_config, - comment_id, - event_type=event_type, - token=app_token, - pull_number=pr_data.get("number"), - node_id=node_id, - ) - - result = await trigger_pr_review_from_ref( - pr_ref, - source="github", - github_login=github_login, - github_user_id=github_user_id, - ) - if not result.get("success"): - logger.warning( - "Failed to trigger reviewer from @open-swe review on %s/%s#%s: %s", - pr_ref.owner, - pr_ref.repo, - pr_ref.number, - result.get("error"), - ) - - async def _fetch_open_pr_for_branch( repo_config: dict[str, str], head_ref: str, *, token: str ) -> dict[str, Any] | None: @@ -2541,20 +2430,9 @@ async def process_github_pr_comment(payload: dict[str, Any], event_type: str) -> else: logger.warning("Failed to persist branch_name metadata for thread %s", thread_id) - comment = payload.get("comment") or payload.get("review", {}) - is_review_request, _pr_url_override = parse_github_review_command(comment.get("body") or "") email = await email_for_login(github_login) or "" if email: github_token = await _get_or_resolve_thread_github_token(thread_id, email) - elif is_review_request: - github_token, expires_at = await get_github_app_installation_token_with_expiry() - if github_token: - try: - await persist_encrypted_github_token(thread_id, github_token, expires_at=expires_at) - except Exception: - logger.warning( - "Could not persist bot token for PR review request thread %s", thread_id - ) else: logger.warning("No email mapping for GitHub user '%s', skipping", github_login) return @@ -3011,24 +2889,11 @@ async def github_webhook(request: Request, background_tasks: BackgroundTasks) -> logger.info("Accepted GitHub PR %s webhook, scheduling auto-review task", action) background_tasks.add_task(process_github_pr_ready, payload) return {"status": "accepted", "message": f"Processing PR {action} for auto-review"} - if not _is_open_swe_reviewer_request(payload): - logger.info("Ignoring PR review request for a different reviewer") - return {"status": "ignored", "reason": "Review request is not for open-swe bot"} - if not await _is_repo_enabled_for_review(webhook_repo_config): - logger.warning( - "Rejecting GitHub reviewer webhook: repo '%s/%s' not enabled for review", - webhook_repo_config.get("owner"), - webhook_repo_config.get("name"), - ) - return {"status": "ignored", "reason": "Repository not enabled for review"} - - gate_rejection = await _enforce_public_repo_org_gate(payload, "pull_request") - if gate_rejection is not None: - return gate_rejection - - logger.info("Accepted GitHub PR review request webhook, scheduling reviewer task") - background_tasks.add_task(process_github_pr_review_request, payload) - return {"status": "accepted", "message": "Processing GitHub PR review request"} + logger.info("Ignoring unsupported GitHub pull_request action: %s", action) + return { + "status": "ignored", + "reason": f"Unsupported GitHub pull_request action: {action}", + } if event_type == "push": if not await _is_repo_enabled_for_review(webhook_repo_config): diff --git a/tests/test_github_issue_webhook.py b/tests/test_github_issue_webhook.py index acc61193..da6cb868 100644 --- a/tests/test_github_issue_webhook.py +++ b/tests/test_github_issue_webhook.py @@ -11,7 +11,6 @@ from fastapi.testclient import TestClient from agent import webapp from agent.tools import request_pr_review as request_pr_review_tool -from agent.utils import github_comments from agent.utils import slack as slack_utils from agent.utils.slack import GitHubPrRef @@ -501,23 +500,8 @@ def test_github_webhook_ignores_unsupported_comment_action(monkeypatch) -> None: } -def test_github_webhook_blocks_reviewer_repo_not_enabled_in_dashboard(monkeypatch) -> None: - called = False - - async def fake_process_github_pr_review_request(payload: dict[str, object]) -> None: - nonlocal called - called = True - - monkeypatch.setattr( - webapp, "process_github_pr_review_request", fake_process_github_pr_review_request - ) +def test_github_webhook_ignores_review_requested(monkeypatch) -> None: monkeypatch.setattr(webapp, "GITHUB_WEBHOOK_SECRET", _TEST_WEBHOOK_SECRET) - - async def fake_is_review_repo_enabled(_owner: str, _name: str) -> bool: - return False - - monkeypatch.setattr(webapp, "is_review_repo_enabled", fake_is_review_repo_enabled) - client = TestClient(webapp.app) response = _post_github_webhook( client, @@ -537,77 +521,10 @@ def test_github_webhook_blocks_reviewer_repo_not_enabled_in_dashboard(monkeypatc ) assert response.status_code == 200 - assert response.json() == {"status": "ignored", "reason": "Repository not enabled for review"} - assert called is False - - -def test_github_webhook_accepts_open_swe_review_requested(monkeypatch) -> None: - called: dict[str, object] = {} - - async def fake_process_github_pr_review_request(payload: dict[str, object]) -> None: - called["payload"] = payload - - monkeypatch.setattr( - webapp, "process_github_pr_review_request", fake_process_github_pr_review_request - ) - monkeypatch.setattr(webapp, "GITHUB_WEBHOOK_SECRET", _TEST_WEBHOOK_SECRET) - - client = TestClient(webapp.app) - response = _post_github_webhook( - client, - "pull_request", - { - "action": "review_requested", - "requested_reviewer": {"login": "open-swe[bot]"}, - "pull_request": { - "number": 1244, - "html_url": "https://github.com/langchain-ai/open-swe/pull/1244", - "base": {"sha": "base-sha"}, - "head": {"sha": "head-sha", "ref": "feature-branch"}, - }, - "repository": {"owner": {"login": "langchain-ai"}, "name": "open-swe"}, - "sender": {"login": "octocat"}, - }, - ) - - assert response.status_code == 200 - assert response.json()["status"] == "accepted" - assert called["payload"]["requested_reviewer"]["login"] == "open-swe[bot]" - - -def test_github_webhook_ignores_review_requested_for_other_reviewer(monkeypatch) -> None: - called = False - - async def fake_process_github_pr_review_request(payload: dict[str, object]) -> None: - nonlocal called - called = True - - monkeypatch.setattr( - webapp, "process_github_pr_review_request", fake_process_github_pr_review_request - ) - monkeypatch.setattr(webapp, "GITHUB_WEBHOOK_SECRET", _TEST_WEBHOOK_SECRET) - - client = TestClient(webapp.app) - response = _post_github_webhook( - client, - "pull_request", - { - "action": "review_requested", - "requested_reviewer": {"login": "someone-else"}, - "pull_request": { - "number": 1244, - "html_url": "https://github.com/langchain-ai/open-swe/pull/1244", - "base": {"sha": "base-sha"}, - "head": {"sha": "head-sha", "ref": "feature-branch"}, - }, - "repository": {"owner": {"login": "langchain-ai"}, "name": "open-swe"}, - "sender": {"login": "octocat"}, - }, - ) - - assert response.status_code == 200 - assert response.json()["status"] == "ignored" - assert called is False + assert response.json() == { + "status": "ignored", + "reason": "Unsupported GitHub pull_request action: review_requested", + } def test_slack_webhook_routes_review_command_to_agent(monkeypatch) -> None: @@ -816,76 +733,9 @@ def test_slack_webhook_threaded_followup_uses_parent_thread_ts(monkeypatch) -> N assert event_data["event_ts"] == "1700000000.000200" -def test_process_slack_pr_review_request_posts_trace_reply(monkeypatch) -> None: +def test_process_github_pr_ready_creates_reviewer_run(monkeypatch) -> None: captured: dict[str, object] = {} - async def fake_trigger_pr_review_from_ref( - pr_ref: GitHubPrRef, - *, - source: str, - github_login: str = "", - github_user_id: int | None = None, - slack_channel_id: str = "", - slack_thread_ts: str = "", - ) -> dict[str, object]: - captured["pr_ref"] = pr_ref - captured["source"] = source - captured["slack_channel_id"] = slack_channel_id - captured["slack_thread_ts"] = slack_thread_ts - return {"success": True, "thread_id": "reviewer-thread-id", "pr_url": pr_ref.url} - - async def fake_post_slack_trace_reply( - channel_id: str, thread_ts: str, thread_id: str, *, include_dashboard_link: bool = True - ) -> None: - captured["trace_reply"] = { - "channel_id": channel_id, - "thread_ts": thread_ts, - "thread_id": thread_id, - "include_dashboard_link": include_dashboard_link, - } - - async def fake_set_slack_assistant_status(channel_id: str, thread_ts: str) -> bool: - captured.setdefault("status_calls", []).append((channel_id, thread_ts)) - return True - - monkeypatch.setattr(webapp, "trigger_pr_review_from_ref", fake_trigger_pr_review_from_ref) - monkeypatch.setattr(webapp, "post_slack_trace_reply", fake_post_slack_trace_reply) - monkeypatch.setattr(webapp, "set_slack_assistant_status", fake_set_slack_assistant_status) - - asyncio.run( - webapp.process_slack_pr_review_request( - GitHubPrRef( - owner="langchain-ai", - repo="open-swe", - number=1244, - url="https://github.com/langchain-ai/open-swe/pull/1244", - ), - "C123", - "1700000000.000100", - ) - ) - - assert captured["source"] == "slack" - assert captured["slack_channel_id"] == "C123" - assert captured["slack_thread_ts"] == "1700000000.000100" - assert captured["trace_reply"] == { - "channel_id": "C123", - "thread_ts": "1700000000.000100", - "thread_id": "reviewer-thread-id", - "include_dashboard_link": False, - } - assert captured["status_calls"] == [ - ("C123", "1700000000.000100"), - ("C123", "1700000000.000100"), - ] - - -def test_process_github_pr_review_request_creates_reviewer_run(monkeypatch) -> None: - captured: dict[str, object] = {} - - async def fake_get_github_app_installation_token() -> str | None: - return "app-token" - async def fake_get_github_app_installation_token_with_expiry() -> tuple[str | None, str | None]: return "app-token", None @@ -919,9 +769,6 @@ def test_process_github_pr_review_request_creates_reviewer_run(monkeypatch) -> N captured["set_metadata_thread_id"] = thread_id captured["set_metadata_kwargs"] = kwargs - monkeypatch.setattr( - webapp, "get_github_app_installation_token", fake_get_github_app_installation_token - ) monkeypatch.setattr( webapp, "get_github_app_installation_token_with_expiry", @@ -935,10 +782,9 @@ def test_process_github_pr_review_request_creates_reviewer_run(monkeypatch) -> N monkeypatch.setattr(webapp, "get_client", lambda url: _FakeLangGraphClient()) asyncio.run( - webapp.process_github_pr_review_request( + webapp.process_github_pr_ready( { - "action": "review_requested", - "requested_reviewer": {"login": "open-swe[bot]"}, + "action": "opened", "pull_request": { "number": 1244, "html_url": "https://github.com/langchain-ai/open-swe/pull/1244", @@ -1158,7 +1004,7 @@ def test_request_pr_review_tool_uses_shared_trigger(monkeypatch) -> None: assert result["success"] is True -def test_process_github_pr_comment_review_request_without_email_uses_app_token( +def test_process_github_pr_comment_without_email_skips( monkeypatch, ) -> None: captured: dict[str, object] = {} @@ -1174,15 +1020,6 @@ def test_process_github_pr_comment_review_request_without_email_uses_app_token( None, ) - async def fake_get_app_token_with_expiry() -> tuple[str, str]: - return "app-token", "2026-01-01T00:00:00Z" - - async def fake_persist_token( - thread_id: str, token: str, *, expires_at: str | None = None - ) -> str: - captured["persisted"] = {"thread_id": thread_id, "token": token, "expires_at": expires_at} - return "encrypted" - async def fake_react(*args, **kwargs) -> bool: captured["reaction_token"] = kwargs["token"] return True @@ -1196,10 +1033,6 @@ def test_process_github_pr_comment_review_request_without_email_uses_app_token( monkeypatch.setattr(webapp, "extract_pr_context", fake_extract_pr_context) monkeypatch.setattr(webapp, "email_for_login", lambda login: asyncio.sleep(0, result=None)) - monkeypatch.setattr( - webapp, "get_github_app_installation_token_with_expiry", fake_get_app_token_with_expiry - ) - monkeypatch.setattr(webapp, "persist_encrypted_github_token", fake_persist_token) monkeypatch.setattr(webapp, "react_to_github_comment", fake_react) monkeypatch.setattr(webapp, "fetch_pr_comments_since_last_tag", fake_fetch_comments) monkeypatch.setattr(webapp, "_trigger_or_queue_run", fake_trigger_or_queue_run) @@ -1214,14 +1047,7 @@ def test_process_github_pr_comment_review_request_without_email_uses_app_token( ) ) - assert captured["reaction_token"] == "app-token" - assert captured["fetch_token"] == "app-token" - assert captured["persisted"] == { - "thread_id": "00000000-0000-0000-0000-000000000001", - "token": "app-token", - "expires_at": "2026-01-01T00:00:00Z", - } - assert captured["triggered"] + assert captured == {} def test_process_github_issue_uses_resolved_user_token_for_reaction(monkeypatch) -> None: @@ -1394,50 +1220,6 @@ def test_process_github_issue_existing_thread_uses_followup_prompt(monkeypatch) assert "## Repository" not in captured["prompt"] -def test_parse_github_review_command_standalone() -> None: - is_review, url = github_comments.parse_github_review_command("@open-swe review") - assert is_review is True - assert url is None - - -def test_parse_github_review_command_with_url() -> None: - is_review, url = github_comments.parse_github_review_command( - "@open-swe review https://github.com/langchain-ai/open-swe/pull/1244" - ) - assert is_review is True - assert url == "https://github.com/langchain-ai/open-swe/pull/1244" - - -def test_parse_github_review_command_case_insensitive_and_aliases() -> None: - assert github_comments.parse_github_review_command("@OpenSWE Review") == (True, None) - assert github_comments.parse_github_review_command("@openswe review") == (True, None) - assert github_comments.parse_github_review_command("@openswe-dev review") == (True, None) - - -def test_parse_github_review_command_freeform_does_not_match() -> None: - assert github_comments.parse_github_review_command("@open-swe review my code") == (False, None) - assert github_comments.parse_github_review_command("@open-swe please review") == (False, None) - assert github_comments.parse_github_review_command("@open-swe fix this") == (False, None) - assert github_comments.parse_github_review_command("") == (False, None) - - -def test_parse_github_review_command_does_not_swallow_trailing_text() -> None: - # Trailing text after `review` (on a new line, with punctuation, or extra - # words) must NOT be parsed as a URL — otherwise the comment would be - # silently dropped instead of falling through to the regular handler. - assert github_comments.parse_github_review_command("@open-swe review\nthanks!") == ( - False, - None, - ) - assert github_comments.parse_github_review_command("@open-swe review please") == (False, None) - assert github_comments.parse_github_review_command("@open-swe review now") == (False, None) - # Non-http schemes are not URLs we can route to a PR. - assert github_comments.parse_github_review_command("@open-swe review ftp://x/y/pull/1") == ( - False, - None, - ) - - def test_github_webhook_routes_pr_comment_review_to_agent(monkeypatch) -> None: captured: dict[str, object] = {} @@ -1445,13 +1227,7 @@ def test_github_webhook_routes_pr_comment_review_to_agent(monkeypatch) -> None: captured["payload"] = payload captured["event_type"] = event_type - async def fake_process_review_command( - payload: dict[str, object], event_type: str, pr_url_override: str | None - ) -> None: - raise AssertionError("review commands should route through the main agent") - monkeypatch.setattr(webapp, "process_github_pr_comment", fake_process_pr_comment) - monkeypatch.setattr(webapp, "process_github_pr_review_command", fake_process_review_command) monkeypatch.setattr(webapp, "GITHUB_WEBHOOK_SECRET", _TEST_WEBHOOK_SECRET) monkeypatch.setattr(webapp, "ALLOWED_GITHUB_ORGS", frozenset({"langchain-ai"})) @@ -1508,105 +1284,3 @@ def test_github_webhook_routes_pr_review_request_comment_to_agent(monkeypatch) - assert response.status_code == 200 assert response.json() == {"status": "accepted", "message": "Processing issue_comment event"} assert captured["event_type"] == "issue_comment" - - -def test_process_github_pr_review_command_uses_payload_pr(monkeypatch) -> None: - captured: dict[str, object] = {} - - async def fake_get_app_token() -> str: - return "app-token" - - async def fake_react(*args, **kwargs) -> None: - captured["reacted"] = True - - async def fake_trigger( - pr_ref: GitHubPrRef, - *, - source: str, - github_login: str = "", - github_user_id: int | None = None, - slack_channel_id: str = "", - slack_thread_ts: str = "", - ) -> dict[str, object]: - captured["pr_ref"] = pr_ref - captured["source"] = source - captured["github_login"] = github_login - captured["github_user_id"] = github_user_id - return {"success": True, "thread_id": "tid", "pr_url": pr_ref.url} - - monkeypatch.setattr(webapp, "get_github_app_installation_token", fake_get_app_token) - monkeypatch.setattr(webapp, "react_to_github_comment", fake_react) - monkeypatch.setattr(webapp, "trigger_pr_review_from_ref", fake_trigger) - - asyncio.run( - webapp.process_github_pr_review_command( - { - "issue": { - "number": 1244, - "html_url": "https://github.com/langchain-ai/open-swe/pull/1244", - "pull_request": {"url": "..."}, - }, - "comment": {"id": 9, "body": "@open-swe review"}, - "repository": {"owner": {"login": "langchain-ai"}, "name": "open-swe"}, - "sender": {"login": "octocat", "id": 123}, - }, - "issue_comment", - None, - ) - ) - - pr_ref = captured["pr_ref"] - assert isinstance(pr_ref, GitHubPrRef) - assert pr_ref.owner == "langchain-ai" - assert pr_ref.repo == "open-swe" - assert pr_ref.number == 1244 - assert pr_ref.url == "https://github.com/langchain-ai/open-swe/pull/1244" - assert captured["source"] == "github" - assert captured["github_login"] == "octocat" - assert captured["github_user_id"] == 123 - assert captured.get("reacted") is True - - -def test_process_github_pr_review_command_uses_url_override(monkeypatch) -> None: - captured: dict[str, object] = {} - - async def fake_get_app_token() -> str: - return "app-token" - - async def fake_react(*args, **kwargs) -> None: - return None - - async def fake_trigger( - pr_ref: GitHubPrRef, - *, - source: str, - github_login: str = "", - github_user_id: int | None = None, - slack_channel_id: str = "", - slack_thread_ts: str = "", - ) -> dict[str, object]: - captured["pr_ref"] = pr_ref - return {"success": True, "thread_id": "tid", "pr_url": pr_ref.url} - - monkeypatch.setattr(webapp, "get_github_app_installation_token", fake_get_app_token) - monkeypatch.setattr(webapp, "react_to_github_comment", fake_react) - monkeypatch.setattr(webapp, "trigger_pr_review_from_ref", fake_trigger) - - asyncio.run( - webapp.process_github_pr_review_command( - { - "issue": {"number": 99, "pull_request": {"url": "..."}}, - "comment": {"id": 1, "body": "@open-swe review https://github.com/x/y/pull/7"}, - "repository": {"owner": {"login": "x"}, "name": "y"}, - "sender": {"login": "octocat", "id": 1}, - }, - "issue_comment", - "https://github.com/x/y/pull/7", - ) - ) - - pr_ref = captured["pr_ref"] - assert isinstance(pr_ref, GitHubPrRef) - assert pr_ref.number == 7 - assert pr_ref.owner == "x" - assert pr_ref.repo == "y" diff --git a/tests/test_public_repo_org_gate.py b/tests/test_public_repo_org_gate.py index fa843e7a..4741bd85 100644 --- a/tests/test_public_repo_org_gate.py +++ b/tests/test_public_repo_org_gate.py @@ -54,15 +54,9 @@ def test_gate_blocks_non_member_on_public_pr_comment(monkeypatch) -> None: _common_setup(monkeypatch) seen = _install_membership_stub(monkeypatch, members={"insider"}) - async def fake_process_github_pr_review_command(*_args, **_kwargs) -> None: - raise AssertionError("should not be called") - async def fake_process_github_pr_comment(*_args, **_kwargs) -> None: raise AssertionError("should not be called") - monkeypatch.setattr( - webapp, "process_github_pr_review_command", fake_process_github_pr_review_command - ) monkeypatch.setattr(webapp, "process_github_pr_comment", fake_process_github_pr_comment) client = TestClient(webapp.app) @@ -254,16 +248,9 @@ def test_gate_blocks_non_member_on_public_issue(monkeypatch) -> None: assert "not a member" in body["reason"] -def test_gate_blocks_non_member_on_public_review_requested(monkeypatch) -> None: +def test_review_requested_is_unsupported_before_public_repo_gate(monkeypatch) -> None: _common_setup(monkeypatch) - _install_membership_stub(monkeypatch, members={"insider"}) - - async def fake_process_github_pr_review_request(*_args, **_kwargs) -> None: - raise AssertionError("should not be called") - - monkeypatch.setattr( - webapp, "process_github_pr_review_request", fake_process_github_pr_review_request - ) + seen = _install_membership_stub(monkeypatch, members={"insider"}) client = TestClient(webapp.app) response = _post_github_webhook( @@ -288,9 +275,11 @@ def test_gate_blocks_non_member_on_public_review_requested(monkeypatch) -> None: ) assert response.status_code == 200 - body = response.json() - assert body["status"] == "ignored" - assert "not a member" in body["reason"] + assert response.json() == { + "status": "ignored", + "reason": "Unsupported GitHub pull_request action: review_requested", + } + assert seen["calls"] == [] def test_gate_allows_internal_bot_sender(monkeypatch) -> None: diff --git a/tests/test_slack_context.py b/tests/test_slack_context.py index 9a65bafb..973a8f22 100644 --- a/tests/test_slack_context.py +++ b/tests/test_slack_context.py @@ -8,9 +8,7 @@ from agent.utils.slack import ( TRACE_REPLY_TIPS, convert_mentions_to_slack_format, format_slack_messages_for_prompt, - looks_like_slack_pr_review_command, parse_github_pr_url, - parse_slack_review_command, post_slack_trace_reply, replace_bot_mention_with_username, select_slack_context_messages, @@ -155,52 +153,6 @@ def test_parse_github_pr_url_slack_formatted_link() -> None: assert pr_ref.number == 1244 -def test_parse_slack_review_command_requires_exact_review_command() -> None: - pr_ref = parse_slack_review_command("review https://github.com/langchain-ai/open-swe/pull/1244") - - assert pr_ref is not None - assert pr_ref.owner == "langchain-ai" - assert pr_ref.repo == "open-swe" - assert pr_ref.number == 1244 - assert ( - parse_slack_review_command( - "please review https://github.com/langchain-ai/open-swe/pull/1244" - ) - is None - ) - assert ( - parse_slack_review_command("review https://github.com/langchain-ai/open-swe/issues/1244") - is None - ) - - -def test_parse_slack_review_command_supports_slack_link() -> None: - pr_ref = parse_slack_review_command( - "review " - ) - - assert pr_ref is not None - assert pr_ref.url == "https://github.com/langchain-ai/open-swe/pull/1244" - - -def test_parse_slack_review_command_supports_slack_wrapped_raw_link() -> None: - pr_ref = parse_slack_review_command( - "review " - ) - - assert pr_ref is not None - assert pr_ref.url == "https://github.com/langchain-ai/open-swe/pull/1244" - - -def test_looks_like_slack_pr_review_command_validates_github_host() -> None: - assert looks_like_slack_pr_review_command( - "review https://github.com/langchain-ai/open-swe/issues/1244" - ) - assert not looks_like_slack_pr_review_command( - "review https://example.com/redirect?next=https://github.com/langchain-ai/open-swe/pull/1244" - ) - - def test_format_slack_messages_for_prompt_uses_name_and_id() -> None: formatted = format_slack_messages_for_prompt( [{"ts": "1.0", "text": "hello", "user": "U123"}],