deer-flow/tests/skills/test_github_research_issues.py
sxl-lucky7 5ca17829be
fix(skills): exclude pull requests from GitHub issue research (#7516)
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-26 15:38:59 +08:00

128 lines
4.3 KiB
Python

"""Keep pull requests out of repository issue research, across both transports."""
import io
import json
import runpy
import sys
import urllib.error
import urllib.request
from pathlib import Path
from urllib.parse import parse_qs, urlsplit
import pytest
@pytest.fixture(params=["requests", "urllib"])
def issue_client(request, monkeypatch):
if request.param == "urllib":
monkeypatch.setitem(sys.modules, "requests", None)
script = (
Path(__file__).resolve().parents[2]
/ "skills/public/github-deep-research/scripts/github_api.py"
)
module = runpy.run_path(str(script))
pages = []
calls = []
if request.param == "urllib":
def fake_urlopen(prepared, timeout):
calls.append(prepared.full_url)
payload, status = pages.pop(0)
data = json.dumps(payload).encode()
if status >= 400:
raise urllib.error.HTTPError(prepared.full_url, status, "error", {}, io.BytesIO(data))
response = io.BytesIO(data)
response.status = status
return response
monkeypatch.setattr(urllib.request, "urlopen", fake_urlopen)
else:
requests = pytest.importorskip("requests")
def fake_send(session, prepared, **kwargs):
calls.append(prepared.url)
payload, status = pages.pop(0)
response = requests.Response()
response.status_code = status
response._content = json.dumps(payload).encode()
return response
monkeypatch.setattr(requests.Session, "send", fake_send)
return module["GitHubAPI"](), pages, calls
def test_issue_limit_counts_issues_instead_of_pull_requests(issue_client):
client, pages, calls = issue_client
pages.extend([
([{"number": 10, "pull_request": {}}, {"number": 9}], 200),
([{"number": 8, "pull_request": {}}, {"number": 7}], 200),
])
assert client.get_issues("owner", "repo", state="open", limit=2, labels="bug,help wanted") == [{"number": 9}, {"number": 7}]
assert len(calls) == 2
for index, url in enumerate(calls):
assert urlsplit(url).path == "/repos/owner/repo/issues"
parameters = parse_qs(urlsplit(url).query)
assert parameters["state"] == ["open"]
assert parameters["labels"] == ["bug,help wanted"]
assert parameters["per_page"] == ["2"]
assert parameters.get("page", ["1"]) == [str(index + 1)]
def test_issue_search_continues_after_a_page_of_only_pull_requests(issue_client):
client, pages, calls = issue_client
pages.extend([
([{"number": 5, "pull_request": {}}, {"number": 4, "pull_request": {}}], 200),
([{"number": 3}], 200),
])
assert client.get_issues("owner", "repo", limit=2) == [{"number": 3}]
assert len(calls) == 2
def test_issue_search_stops_at_an_empty_page(issue_client):
client, pages, calls = issue_client
pages.extend([([{"number": 2, "pull_request": {}}], 200), ([], 200)])
assert client.get_issues("owner", "repo", limit=1) == []
assert len(calls) == 2
def test_issue_search_does_not_fetch_an_unneeded_page(issue_client):
client, pages, calls = issue_client
pages.append(([{"number": 2}, {"number": 1}], 200))
assert client.get_issues("owner", "repo", limit=2) == [{"number": 2}, {"number": 1}]
assert len(calls) == 1
def test_last_page_does_not_exceed_the_requested_issue_limit(issue_client):
client, pages, calls = issue_client
pages.extend([
([{"number": 4}, {"number": 3, "pull_request": {}}], 200),
([{"number": 2}, {"number": 1}], 200),
])
assert client.get_issues("owner", "repo", limit=2) == [{"number": 4}, {"number": 2}]
assert len(calls) == 2
@pytest.mark.parametrize("limit", [0, -1])
def test_nonpositive_issue_limit_does_not_make_a_request(issue_client, limit):
client, pages, calls = issue_client
pages.append(([], 200))
assert client.get_issues("owner", "repo", limit=limit) == []
assert calls == []
def test_later_page_failure_is_not_returned_as_partial_research(issue_client):
client, pages, calls = issue_client
pages.extend([([{"number": 3}, {"number": 2, "pull_request": {}}], 200), ({"message": "rate limited"}, 403)])
with pytest.raises(Exception, match="403"):
client.get_issues("owner", "repo", limit=2)
assert len(calls) == 2