diff --git a/.github/workflows/func-tests-live.yaml b/.github/workflows/func-tests-live.yaml index d2242620..b51f9530 100644 --- a/.github/workflows/func-tests-live.yaml +++ b/.github/workflows/func-tests-live.yaml @@ -36,11 +36,13 @@ jobs: shell: bash env: # Two tokens with different scopes: - # - _CI exercises read-only / pull-scoped endpoints - # (scopes-send, junit-process, queue status, queue show, - # ci git-refs / queue-info). - # - _ADMIN exercises destructive queue-admin endpoints - # (queue pause / unpause). + # - _CI exercises CI-tooling endpoints (scopes-send, + # junit-process). ci git-refs / queue-info are + # locally evaluated and need no token at all. + # - _ADMIN exercises every endpoint under /merge-queue/ + # (status, show, pause, unpause). The CI token is + # rejected with 403 on these, so they all share the + # queue-management-scoped admin token. # Tests select the appropriate fixture; absent tokens # cause individual tests to skip rather than fail. LIVE_TEST_MERGIFY_TOKEN_CI: ${{ secrets.MERGIFY_CLI_LIVE_TEST_MERGIFY_TOKEN_CI }} diff --git a/func-tests/conftest.py b/func-tests/conftest.py index eca552c8..e7e33943 100644 --- a/func-tests/conftest.py +++ b/func-tests/conftest.py @@ -75,11 +75,14 @@ class CliResult: @pytest.fixture def live_token() -> str: - """Token for read-only / pull-scoped live endpoints. + """Token for CI-integration live endpoints. Skips the test if ``LIVE_TEST_MERGIFY_TOKEN_CI`` isn't set. - Use this for everything that doesn't need queue-admin rights - (scopes-send, junit-process, queue status / show, etc.). + Use this for CI-tooling endpoints that don't touch the queue: + ``ci scopes-send`` and ``ci junit-process``. Every endpoint + under ``/merge-queue/`` (status, show, pause, unpause) + requires queue-management scope and rejects the CI token + with 403, so those tests must use [`live_admin_token`]. """ token = os.environ.get("LIVE_TEST_MERGIFY_TOKEN_CI", "").strip() if not token: diff --git a/func-tests/test_live_smoke.py b/func-tests/test_live_smoke.py index ca7047be..7e2948fc 100644 --- a/func-tests/test_live_smoke.py +++ b/func-tests/test_live_smoke.py @@ -99,6 +99,56 @@ def test_queue_pause_unpause_roundtrip( ) +def test_queue_status( + live_admin_token: str, + cli: typing.Callable[..., typing.Any], +) -> None: + """`GET /v1/repos/{owner}/{repo}/merge-queue/status`. + + Uses the admin-scoped token because all queue endpoints + (read or write) require queue-management scope on the test + repo; the CI-scoped token is rejected with 403. + + ``--json`` mode is a passthrough of the API response, so the + smoke test only checks that the call succeeds and parses as + JSON — the contract we want preserved across the Python → + Rust port is the URL, the auth, and that the response is + valid JSON. + """ + import json + + # Group-level options (``--token`` / ``--api-url`` / + # ``--repository``) come BEFORE the subcommand. Click requires + # this for the Python implementation (the options live on the + # ``@queue`` group); Rust accepts both orders via clap's + # ``global = true``. Put them on the group so the same test + # works against both ends of the port. + result = cli( + "queue", + "--api-url", + API_URL, + "--token", + live_admin_token, + "--repository", + REPOSITORY, + "status", + "--json", + ) + assert result.returncode == 0, ( + f"queue status failed\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}" + ) + try: + payload = json.loads(result.stdout) + except json.JSONDecodeError as exc: + pytest.fail( + f"queue status --json emitted non-JSON output\n" + f"error: {exc}\nstdout:\n{result.stdout}", + ) + assert isinstance(payload, dict), ( + f"queue status --json must emit a JSON object\nstdout:\n{result.stdout}" + ) + + def test_ci_git_refs_fallback( cli: typing.Callable[..., typing.Any], ) -> None: