"""Opt-in integration tests against a real (containerized) Gitea (#66). Skipped by default. Enable with GITEA_INTEGRATION=1 plus a local instance and token — see tests/integration/README.md. These prove real Gitea behavior that the mocked unit suite can only simulate: pagination, targeted label edits, permission fail-closed, and error payload surfacing — all through the shared client (``gitea_auth.api_request`` / ``api_get_all``, #65/#67 shape). No production credentials; tokens never appear in output or assertions. """ import os import sys from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent)) from gitea_auth import api_request, api_get_all # noqa: E402 pytestmark = pytest.mark.skipif( os.environ.get("GITEA_INTEGRATION") != "1", reason="integration tests are opt-in: set GITEA_INTEGRATION=1 (see tests/integration/README.md)", ) # --- issue listing + pagination -------------------------------------------- N_ISSUES = 7 # > page_size below, forcing a real multi-page walk @pytest.fixture(scope="module") def issues(gitea, seed_repo): created = [] for i in range(N_ISSUES): created.append(api_request( "POST", f"{seed_repo['api']}/issues", gitea["auth"], payload={"title": f"pagination seed {i}"})) return created def test_issue_pagination_walks_all_pages(gitea, seed_repo, issues): got = api_get_all(f"{seed_repo['api']}/issues?state=open", gitea["auth"], page_size=3) titles = {i["title"] for i in got} assert {f"pagination seed {i}" for i in range(N_ISSUES)} <= titles assert len(got) >= N_ISSUES def test_issue_pagination_honors_overall_limit(gitea, seed_repo, issues): got = api_get_all(f"{seed_repo['api']}/issues?state=open", gitea["auth"], page_size=3, limit=4) assert len(got) == 4 # --- PR listing -------------------------------------------------------------- def test_pr_listing_via_shared_client(gitea, seed_repo, issues): # Create a branch (via the contents API) and a real PR, then list. api_request( "POST", f"{seed_repo['api']}/contents/inttest.txt", gitea["auth"], payload={"content": "aW50ZWdyYXRpb24gc2VlZAo=", # "integration seed" "message": "seed PR branch", "new_branch": "inttest-pr"}) pr = api_request( "POST", f"{seed_repo['api']}/pulls", gitea["auth"], payload={"title": "integration seed PR", "head": "inttest-pr", "base": "main"}) got = api_get_all(f"{seed_repo['api']}/pulls?state=open", gitea["auth"], page_size=1) assert any(p["number"] == pr["number"] for p in got) # --- targeted label add/remove ---------------------------------------------- def test_targeted_label_add_and_remove(gitea, seed_repo, issues): labels = {} for name, color in (("keep-me", "#00ff00"), ("drop-me", "#ff0000")): labels[name] = api_request( "POST", f"{seed_repo['api']}/labels", gitea["auth"], payload={"name": name, "color": color}) issue_no = issues[0]["number"] issue_labels_url = f"{seed_repo['api']}/issues/{issue_no}/labels" api_request("POST", issue_labels_url, gitea["auth"], payload={"labels": [labels["keep-me"]["id"], labels["drop-me"]["id"]]}) # Targeted removal of one label must not disturb the other. api_request("DELETE", f"{issue_labels_url}/{labels['drop-me']['id']}", gitea["auth"]) remaining = {l["name"] for l in api_request("GET", issue_labels_url, gitea["auth"])} assert "keep-me" in remaining assert "drop-me" not in remaining # --- permission denial / fail-closed ----------------------------------------- def test_bad_token_fails_closed_without_leaking_it(gitea): bogus = "token 0123456789abcdef0123456789abcdef01234567" with pytest.raises(RuntimeError) as exc: api_request("GET", f"{gitea['base']}/api/v1/user", bogus) msg = str(exc.value) assert "401" in msg assert "0123456789abcdef" not in msg # credential never echoed def test_real_error_payload_surfaces_safely(gitea, seed_repo): # A genuinely missing resource: Gitea's real 404 error payload must become # a clear RuntimeError, not a stack trace, and must not echo the token. with pytest.raises(RuntimeError) as exc: api_request("GET", f"{seed_repo['api']}/issues/999999", gitea["auth"]) msg = str(exc.value) assert "404" in msg token = os.environ.get("GITEA_INTEGRATION_TOKEN", "") if token: assert token not in msg