nisse-review: one unlistable repository holds up no other
Found at bring-up: an empty private repository's /pulls answered 404, and intake stopped there, so the whole round failed and nothing was reviewed. A repository whose PRs can't be listed is now logged (intake.repo_failed) and skipped for the round; its items stay as they are. Also, from nisse-review's review of this PR: a two-page test pins the search listing the patterns rest on. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
This commit is contained in:
1 parent
faf973be6c
commit
6e697b55a8
4 files changed
+43
-1
No files matched your search
@@ -375,6 +375,23 @@ def test_visible_repositories_read_the_search_and_a_404_is_not_found() -> None:
|
|||||||
gitea.repository("d/gone")
|
gitea.repository("d/gone")
|
||||||
|
|
||||||
|
|
||||||
|
def test_visible_repositories_page_through_the_search() -> None:
|
||||||
|
# The patterns rest on the search listing everything (#154's review).
|
||||||
|
asked: list[dict[str, str]] = []
|
||||||
|
|
||||||
|
def handler(request: httpx.Request) -> httpx.Response:
|
||||||
|
asked.append(dict(request.url.params))
|
||||||
|
page = int(request.url.params["page"])
|
||||||
|
count = PR_PAGE_SIZE if page == 1 else 3
|
||||||
|
data = [{"full_name": f"o/r{page}-{n}"} for n in range(count)]
|
||||||
|
return httpx.Response(200, json={"ok": True, "data": data})
|
||||||
|
|
||||||
|
found = client(handler).visible_repositories()
|
||||||
|
assert len(found) == PR_PAGE_SIZE + 3 and found[-1] == "o/r2-2"
|
||||||
|
assert [a["page"] for a in asked] == ["1", "2"]
|
||||||
|
assert all(a["limit"] == str(PR_PAGE_SIZE) for a in asked)
|
||||||
|
|
||||||
|
|
||||||
def test_a_pull_request_carries_what_a_review_needs() -> None:
|
def test_a_pull_request_carries_what_a_review_needs() -> None:
|
||||||
def handler(request: httpx.Request) -> httpx.Response:
|
def handler(request: httpx.Request) -> httpx.Response:
|
||||||
if request.url.path.endswith(".diff"):
|
if request.url.path.endswith(".diff"):
|
||||||
|
|||||||
@@ -106,8 +106,16 @@ class Scheduler:
|
|||||||
repos = reviewed(self._gitea, self._patterns, self._oplog)
|
repos = reviewed(self._gitea, self._patterns, self._oplog)
|
||||||
open_items = {i.id: i for i in self._store.work_items("open")}
|
open_items = {i.id: i for i in self._store.work_items("open")}
|
||||||
for repo in repos:
|
for repo in repos:
|
||||||
|
try:
|
||||||
|
prs = self._gitea.open_pull_requests(repo)
|
||||||
|
except GiteaError as exc:
|
||||||
|
# One repository Gitea won't list (an empty one, one without
|
||||||
|
# pull requests) holds up none of the others; its items stay
|
||||||
|
# as they are.
|
||||||
|
self._oplog.emit("intake.repo_failed", cause=str(exc)[:300], repo=repo)
|
||||||
|
continue
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
for pr in self._gitea.open_pull_requests(repo):
|
for pr in prs:
|
||||||
key = item_id(repo, pr.number)
|
key = item_id(repo, pr.number)
|
||||||
seen.add(key)
|
seen.add(key)
|
||||||
self._see(open_items.get(key), repo, pr)
|
self._see(open_items.get(key), repo, pr)
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class FakeGitea:
|
|||||||
down: bool = False
|
down: bool = False
|
||||||
# Readable but not given (public, no grant): missing from /user/repos.
|
# Readable but not given (public, no grant): missing from /user/repos.
|
||||||
public: set[str] = field(default_factory=set)
|
public: set[str] = field(default_factory=set)
|
||||||
|
unlisted: set[str] = field(default_factory=set) # its PRs can't be listed
|
||||||
|
|
||||||
def _up(self) -> None:
|
def _up(self) -> None:
|
||||||
if self.down:
|
if self.down:
|
||||||
@@ -99,6 +100,8 @@ class FakeGitea:
|
|||||||
|
|
||||||
def open_pull_requests(self, repo: str) -> list[PullRequest]:
|
def open_pull_requests(self, repo: str) -> list[PullRequest]:
|
||||||
self._up()
|
self._up()
|
||||||
|
if repo in self.unlisted:
|
||||||
|
raise GiteaNotFound(f"gitea GET /repos/{repo}/pulls returned 404")
|
||||||
return list(self.repos[repo])
|
return list(self.repos[repo])
|
||||||
|
|
||||||
def pull_request(self, repo: str, index: int) -> PullRequest:
|
def pull_request(self, repo: str, index: int) -> PullRequest:
|
||||||
|
|||||||
@@ -328,6 +328,20 @@ def test_patterns_reach_public_repositories_the_account_was_not_given(
|
|||||||
assert w["store"].work_item("infra/tofu#3") is None
|
assert w["store"].work_item("infra/tofu#3") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_repository_whose_prs_cannot_be_listed_holds_up_no_other(
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
# Found at bring-up: an empty private repository's /pulls was a 404,
|
||||||
|
# and the round stopped there, so no repository was reviewed.
|
||||||
|
gitea = FakeGitea(repos={"a/empty": [], REPO: [pr()]}, unlisted={"a/empty"})
|
||||||
|
out = io.StringIO()
|
||||||
|
w = world(tmp_path, Scripted(*REVIEW), gitea)
|
||||||
|
w["scheduler"]._oplog = OpLog("t", out)
|
||||||
|
assert w["scheduler"].round() is True # REPO's PR was reviewed
|
||||||
|
assert item(w).position == REVIEWED
|
||||||
|
assert '"intake.repo_failed"' in out.getvalue() and "a/empty" in out.getvalue()
|
||||||
|
|
||||||
|
|
||||||
def test_a_configured_repository_it_cannot_read_is_left_out() -> None:
|
def test_a_configured_repository_it_cannot_read_is_left_out() -> None:
|
||||||
gitea = FakeGitea(repos={REPO: []})
|
gitea = FakeGitea(repos={REPO: []})
|
||||||
out = io.StringIO()
|
out = io.StringIO()
|
||||||
|
|||||||
Reference in new issue
Block a user