306 lines
9.9 KiB
Python
306 lines
9.9 KiB
Python
import asyncio
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from src import gitea_proxy
|
|
from src import main
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_work_collections_include_supported_review_request_search(monkeypatch):
|
|
requested_paths = []
|
|
|
|
async def fake_fetch(path):
|
|
requested_paths.append(path)
|
|
return []
|
|
|
|
monkeypatch.setattr(gitea_proxy, "fetch", fake_fetch)
|
|
|
|
assert await gitea_proxy.issues() == []
|
|
assert await gitea_proxy.pull_requests() == []
|
|
assert requested_paths == [
|
|
"repos/issues/search?state=open&assigned=true&type=issues&limit=50",
|
|
"repos/issues/search?state=open&assigned=true&type=pulls&limit=50",
|
|
"repos/issues/search?state=open&review_requested=true&type=pulls&limit=50",
|
|
]
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_pull_requests_merge_assignment_and_review_responsibilities(monkeypatch):
|
|
assigned = {
|
|
"id": 11,
|
|
"number": 7,
|
|
"title": "Review API",
|
|
"repository": {"full_name": "stackchain/api"},
|
|
}
|
|
review_only = {
|
|
"id": 12,
|
|
"number": 8,
|
|
"title": "Review mobile",
|
|
"repository": {"full_name": "stackchain/mobile"},
|
|
}
|
|
|
|
async def fake_fetch(path):
|
|
if "assigned=true" in path:
|
|
return [assigned]
|
|
return [assigned.copy(), review_only]
|
|
|
|
monkeypatch.setattr(gitea_proxy, "fetch", fake_fetch)
|
|
|
|
pulls = await gitea_proxy.pull_requests()
|
|
|
|
assert [pull["id"] for pull in pulls] == [11, 12]
|
|
assert pulls[0]["work_reasons"] == ["assigned_to_me", "review_requested"]
|
|
assert pulls[1]["work_reasons"] == ["review_requested"]
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_pull_request_searches_start_concurrently(monkeypatch):
|
|
started = [asyncio.Event(), asyncio.Event()]
|
|
release = asyncio.Event()
|
|
|
|
async def fake_fetch(path):
|
|
index = 0 if "assigned=true" in path else 1
|
|
started[index].set()
|
|
await release.wait()
|
|
return []
|
|
|
|
monkeypatch.setattr(gitea_proxy, "fetch", fake_fetch)
|
|
task = asyncio.create_task(gitea_proxy.pull_requests())
|
|
try:
|
|
await asyncio.wait_for(
|
|
asyncio.gather(*(event.wait() for event in started)), timeout=1
|
|
)
|
|
finally:
|
|
release.set()
|
|
|
|
assert await task == []
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_requested_review_guard_uses_only_dedicated_review_search(monkeypatch):
|
|
requested_paths = []
|
|
|
|
async def fake_fetch(path):
|
|
requested_paths.append(path)
|
|
return [
|
|
{
|
|
"number": 7,
|
|
"repository": {"full_name": "stackchain/api"},
|
|
},
|
|
{
|
|
"number": 8,
|
|
"repository": {"full_name": "stackchain/api"},
|
|
},
|
|
]
|
|
|
|
monkeypatch.setattr(gitea_proxy, "fetch", fake_fetch)
|
|
|
|
assert await gitea_proxy.is_requested_review("stackchain/api", 7) is True
|
|
assert await gitea_proxy.is_requested_review("stackchain/api", 9) is False
|
|
assert await gitea_proxy.is_requested_review("stackchain/private", 7) is False
|
|
assert requested_paths == [
|
|
"repos/issues/search?state=open&review_requested=true&type=pulls&limit=50",
|
|
"repos/issues/search?state=open&review_requested=true&type=pulls&limit=50",
|
|
"repos/issues/search?state=open&review_requested=true&type=pulls&limit=50",
|
|
]
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_context_preserves_repository_and_update_time_for_cross_repo_work(monkeypatch):
|
|
async def user():
|
|
return {"id": 1, "login": "timmy"}
|
|
|
|
async def repositories():
|
|
return []
|
|
|
|
async def issues():
|
|
return [{
|
|
"id": 10,
|
|
"number": 7,
|
|
"title": "Ship mobile flow",
|
|
"state": "open",
|
|
"labels": [{"name": "P0"}],
|
|
"assignees": [{"login": "timmy"}],
|
|
"repository": {"full_name": "stackchain/mobile"},
|
|
"updated_at": "2026-08-06T12:00:00Z",
|
|
"html_url": "https://forge.example/stackchain/mobile/issues/7",
|
|
}]
|
|
|
|
async def pulls():
|
|
return [{
|
|
"id": 11,
|
|
"number": 7,
|
|
"title": "Review API",
|
|
"state": "open",
|
|
"user": {"login": "alex"},
|
|
"labels": [{"name": "priority-high"}],
|
|
"assignees": [{"login": "timmy"}],
|
|
"work_reasons": ["assigned_to_me", "review_requested"],
|
|
"repository": {"full_name": "stackchain/api"},
|
|
"updated_at": "2026-08-06T11:00:00Z",
|
|
"html_url": "https://forge.example/stackchain/api/pulls/7",
|
|
}]
|
|
|
|
monkeypatch.setattr(main, "current_user", user)
|
|
monkeypatch.setattr(main, "repos", repositories)
|
|
monkeypatch.setattr(main, "issues", issues)
|
|
monkeypatch.setattr(main, "pull_requests", pulls)
|
|
|
|
payload = json.loads((await main.context()).body)
|
|
|
|
assert payload["issues"][0]["repository"] == "stackchain/mobile"
|
|
assert payload["issues"][0]["updated_at"] == "2026-08-06T12:00:00Z"
|
|
assert payload["pull_requests"][0]["repository"] == "stackchain/api"
|
|
assert payload["pull_requests"][0]["updated_at"] == "2026-08-06T11:00:00Z"
|
|
assert payload["pull_requests"][0]["labels"] == ["priority-high"]
|
|
assert payload["pull_requests"][0]["assignees"] == ["timmy"]
|
|
assert payload["pull_requests"][0]["work_reasons"] == [
|
|
"assigned_to_me",
|
|
"review_requested",
|
|
]
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_pull_review_detail_combines_pr_files_status_and_reviews(monkeypatch):
|
|
requested_paths = []
|
|
|
|
async def fake_fetch(path):
|
|
requested_paths.append(path)
|
|
if path.endswith("/pulls/7"):
|
|
return {
|
|
"title": "Review API",
|
|
"body": "Please check the retry flow.",
|
|
"html_url": "https://forge.example/stackchain/api/pulls/7",
|
|
"user": {"login": "alex"},
|
|
"head": {"sha": "abc123"},
|
|
}
|
|
if path.endswith("/files"):
|
|
return [
|
|
{"filename": "src/api.py", "status": "modified", "additions": 8, "deletions": 2},
|
|
None,
|
|
]
|
|
if "/commits/abc123/status" in path:
|
|
return {"state": "success"}
|
|
return [
|
|
{"user": {"login": "sam"}, "state": "APPROVED", "body": "Looks good"},
|
|
"malformed",
|
|
]
|
|
|
|
monkeypatch.setattr(gitea_proxy, "fetch", fake_fetch)
|
|
|
|
async def fake_fetch_text(path, max_bytes):
|
|
assert path == "repos/stackchain/api/pulls/7.diff"
|
|
assert max_bytes == gitea_proxy.REVIEW_DIFF_MAX_BYTES
|
|
return "", False
|
|
|
|
monkeypatch.setattr(gitea_proxy, "fetch_text", fake_fetch_text)
|
|
|
|
detail = await gitea_proxy.pull_review_detail("stackchain/api", 7)
|
|
|
|
assert requested_paths == [
|
|
"repos/stackchain/api/pulls/7",
|
|
"repos/stackchain/api/pulls/7/files",
|
|
"repos/stackchain/api/commits/abc123/status",
|
|
"repos/stackchain/api/pulls/7/reviews",
|
|
]
|
|
assert detail["author"] == "alex"
|
|
assert detail["head_sha"] == "abc123"
|
|
assert detail["ci_state"] == "success"
|
|
assert detail["files"][0]["filename"] == "src/api.py"
|
|
assert detail["reviews"][0]["state"] == "APPROVED"
|
|
assert len(detail["files"]) == 1
|
|
assert len(detail["reviews"]) == 1
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_pull_review_detail_fetches_independent_head_resources_concurrently(monkeypatch):
|
|
started = {name: asyncio.Event() for name in ("files", "status", "reviews", "diff")}
|
|
release = asyncio.Event()
|
|
|
|
async def wait_for_release(name, result):
|
|
started[name].set()
|
|
await release.wait()
|
|
return result
|
|
|
|
async def fake_fetch(path):
|
|
if path.endswith("/pulls/7"):
|
|
return {"head": {"sha": "abc123"}, "user": {"login": "alex"}}
|
|
if path.endswith("/files"):
|
|
return await wait_for_release("files", [])
|
|
if path.endswith("/reviews"):
|
|
return await wait_for_release("reviews", [])
|
|
return await wait_for_release("status", {"state": "success"})
|
|
|
|
async def fake_fetch_text(path, max_bytes):
|
|
return await wait_for_release("diff", ("", False))
|
|
|
|
monkeypatch.setattr(gitea_proxy, "fetch", fake_fetch)
|
|
monkeypatch.setattr(gitea_proxy, "fetch_text", fake_fetch_text)
|
|
|
|
detail_task = asyncio.create_task(
|
|
gitea_proxy.pull_review_detail("stackchain/api", 7)
|
|
)
|
|
try:
|
|
await asyncio.wait_for(
|
|
asyncio.gather(*(event.wait() for event in started.values())), timeout=1
|
|
)
|
|
finally:
|
|
release.set()
|
|
|
|
detail = await detail_task
|
|
assert detail["head_sha"] == "abc123"
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_pull_review_detail_attaches_bounded_per_file_diff_previews(monkeypatch):
|
|
async def fake_fetch(path):
|
|
if path.endswith("/pulls/7"):
|
|
return {
|
|
"title": "Review API",
|
|
"head": {"sha": "abc123"},
|
|
"user": {"login": "alex"},
|
|
}
|
|
if path.endswith("/files"):
|
|
return [
|
|
{"filename": "src/api.py", "status": "modified"},
|
|
{"filename": "assets/logo.png", "status": "modified"},
|
|
]
|
|
if path.endswith("/reviews"):
|
|
return []
|
|
return {"state": "success"}
|
|
|
|
diff = """diff --git a/src/api.py b/src/api.py
|
|
index 123..456 100644
|
|
--- a/src/api.py
|
|
+++ b/src/api.py
|
|
@@ -1,2 +1,3 @@
|
|
context
|
|
-old <value>
|
|
+new <value>
|
|
diff --git a/assets/logo.png b/assets/logo.png
|
|
Binary files a/assets/logo.png and b/assets/logo.png differ
|
|
"""
|
|
|
|
async def fake_fetch_text(path, max_bytes):
|
|
assert path == "repos/stackchain/api/pulls/7.diff"
|
|
return diff, True
|
|
|
|
monkeypatch.setattr(gitea_proxy, "fetch", fake_fetch)
|
|
monkeypatch.setattr(gitea_proxy, "fetch_text", fake_fetch_text)
|
|
|
|
detail = await gitea_proxy.pull_review_detail("stackchain/api", 7)
|
|
|
|
source, binary = detail["files"]
|
|
assert source["diff_lines"] == [
|
|
"@@ -1,2 +1,3 @@",
|
|
" context",
|
|
"-old <value>",
|
|
"+new <value>",
|
|
]
|
|
assert source["diff_truncated"] is True
|
|
assert source["diff_available"] is True
|
|
assert binary["diff_available"] is False
|
|
assert binary["diff_binary"] is True
|