"""nisse-review against the real services (Part R, M7.3): a PR in a fresh test repository gets one COMMENT review from the review account, by the local model; an account that could write to a repository is refused. Needs NISSE_REVIEW_GITEA_TOKEN, the review account's token (CI secret; .env locally), besides what every live test needs. """ import io import os import uuid from pathlib import Path import pytest from e2e_support import e2e_roles, fresh_repo, operator_gitea from nisse_core.gitea import GiteaClient from nisse_core.oplog import OpLog from nisse_dev.config import AgentConfig from nisse_review.build import StartupError, build from nisse_review.config import ReviewConfig from nisse_review.review import marker # Smoke: every PR runs it (about a minute on the local model), the only # check that the review account, its token and its read-only refusal work. pytestmark = [pytest.mark.integration, pytest.mark.e2e, pytest.mark.smoke] def review_gitea(url: str) -> GiteaClient: token = os.environ.get("NISSE_REVIEW_GITEA_TOKEN", "") assert token.strip(), "NISSE_REVIEW_GITEA_TOKEN is not set: the review account's" return GiteaClient(url, token) def test_a_ready_pr_gets_one_comment_review( live_config: AgentConfig, tmp_path: Path ) -> None: run = uuid.uuid4().hex[:8] operator = operator_gitea(live_config.gitea_url) reviewer = review_gitea(live_config.gitea_url) account = reviewer.whoami() roles = e2e_roles() with fresh_repo( operator, f"review-{run}", tmp_path / "keys", agent=account ) as repo: tree = { "state_db": str(tmp_path / "review" / "state.db"), "llm": { "base_url": live_config.llm_base_url, "token": live_config.llm_token, }, "roles": {"reviewer": roles.driver, "expert": roles.expert}, "gitea": { "url": live_config.gitea_url, "token": os.environ["NISSE_REVIEW_GITEA_TOKEN"], "operators": ["nisse-testing-operator"], }, "repositories": [repo.name], "tuning": {"review": {"max_turns": 25}}, } config = ReviewConfig.from_tree(tree, {}) # make_repo gives the agent write access: the reviewer refuses that. with pytest.raises(StartupError, match="read access only"): build(config, gitea=reviewer, oplog=OpLog("t", io.StringIO())) operator._request( "PUT", f"/repos/{repo.name}/collaborators/{account}", json={"permission": "read"}, ) operator.branch_with_file( repo.name, base="main", branch="shout", path="sandbox/shout.py", text='def shout(text):\n return text.upper() + "!"\n', message="Add shout", ) pr = operator.open_pull_request( repo.name, head="shout", base="main", title="Add shout", body="Adds shout(text): the text in capitals, with an exclamation mark.", ) built = build(config, gitea=reviewer, oplog=OpLog("t", io.StringIO())) try: assert built.scheduler.round() is True assert built.scheduler.round() is False # reviewed once, not again finally: built.store.close() head = operator.pull_request(repo.name, pr.number).head_sha mine = [ r for r in operator.reviews(repo.name, pr.number) if r.author == account ] assert len(mine) == 1 assert mine[0].state == "COMMENT" and mine[0].commit == head assert marker(head) in mine[0].body