tests.test_setup_guide module¶
Tests for the setup guide issue lifecycle and step rendering.
The setup guide fans out to the GitHub and PyPI APIs: the PAT permission
probe, the repository-settings checks, and an org-type lookup. Every seam is
patched here through _offline_setup_guide, so the suite runs offline
and fast while the real issue-body assembly (template rendering, per-step
collapse state, and the close/keep-open decision) stays under test.
- tests.test_setup_guide.MISSING_ALERTS_PAT = PatPermissionResults(administration=(True, 'Administration: token has access'), contents=(True, 'Contents: token has access'), issues=(True, 'Issues: token has access'), pull_requests=(True, 'Pull requests: token has access'), vulnerability_alerts=(False, "Token lacks 'Dependabot alerts: Read-only' permission. Update the PAT to include this permission."), workflows=(True, 'Workflows: token has access'))¶
A PAT passing every probe but the Dependabot-alerts one.
- tests.test_setup_guide.FULLY_CONFIGURED = {'HAS_CLOUDFLARE_API_TOKEN': 'true'}¶
Secrets a fully-configured repository holds, beyond what
_invokesets.These tests run against this repository’s own
pyproject.toml, which declaressite.deploy = "cloudflare-pages"while it dogfoods that lane, so the step asking for the Cloudflare credentials applies to them. A test asserting that everything passes has to supply them, or it asserts the state of this repository’s secrets rather than the behaviour it means to cover.
- tests.test_setup_guide.test_setup_guide_no_pat_opens_setup_issue()[source]¶
When no PAT is configured, the setup issue opens.
- tests.test_setup_guide.test_setup_guide_pat_without_repo_keeps_issue_open()[source]¶
PAT without –repo cannot verify Dependabot or branch settings.
- tests.test_setup_guide.test_setup_guide_body_contains_template()[source]¶
The setup body carries the token step’s own content, not just its title.
Anchored on the secret name and on a row of the permission table, the two things that step exists to convey. It used to assert on a sentence of navigation prose, which made trimming the step read as a regression.
- tests.test_setup_guide.test_setup_guide_disabled_skips(mock_lifecycle, _mock_token, tmp_path, monkeypatch)[source]¶
When setup-guide is disabled in config, the command exits without action.
- tests.test_setup_guide.test_setup_guide_lifecycle_reflects_check_outcomes(branch_ok, has_vt_key, expected_has_issues)[source]¶
The issue closes only when every verifiable step passes.
All checks pass by default, so the issue closes; a failing branch ruleset or a missing VirusTotal key (with Nuitka active in this repo) keeps it open. An indeterminate ruleset probe (an unreadable API) counts as incomplete too, so the step keeps prompting rather than quietly passing.
- tests.test_setup_guide.test_setup_guide_pat_missing_permission_keeps_issue_open()[source]¶
When PAT is configured but a permission is missing, the issue stays open.
- tests.test_setup_guide.test_setup_guide_pat_missing_permission_body_contains_warning()[source]¶
When PAT has missing permissions, the issue body contains a warning section.
- tests.test_setup_guide.test_setup_guide_completed_step_collapsed()[source]¶
Completed steps render as collapsed details blocks with a checkmark.
- tests.test_setup_guide.test_setup_guide_incomplete_step_expanded()[source]¶
Incomplete steps render as open details blocks with an error indicator.
- tests.test_setup_guide.MISSING_ADMIN_PAT = PatPermissionResults(administration=(False, "Token lacks 'Administration: Read-only' permission. Update the PAT to include this permission."), contents=(True, 'Contents: token has access'), issues=(True, 'Issues: token has access'), pull_requests=(True, 'Pull requests: token has access'), vulnerability_alerts=(True, 'Dependabot alerts: token has access, alerts enabled'), workflows=(True, 'Workflows: token has access'))¶
A PAT passing every probe but the Administration one.
- tests.test_setup_guide.UNREADABLE = (None, 'Check skipped (could not query).')¶
What an Administration-scoped probe answers to a token without it.
- tests.test_setup_guide.test_setup_guide_unverifiable_step_renders_with_reason(title, fork_pr_ok, sha_pinning_ok)[source]¶
A check that could not run keeps its step, and says why.
Both settings are read through Administration-scoped endpoints. Deleting the step on a 403 removed the only notice that the setting is unverified, and hid the token gap itself: a missing Administration permission has no other symptom. The step now renders indeterminate, naming what to fix.
The seam left at
Nonekeeps its passing default, so each case exercises one unreadable probe beside one that resolved.
- tests.test_setup_guide.test_setup_guide_unverifiable_step_does_not_block_closing()[source]¶
An unreadable probe leaves the issue closable.
The reader cannot satisfy a check that never runs, so holding the issue open on it would be the same trap the PyPI publisher gate used to set.
- tests.test_setup_guide.test_setup_guide_disabled_setting_keeps_issue_open(fork_pr_ok, sha_pinning_ok)[source]¶
A readable probe reporting the setting off holds the issue open.
The counterpart to the unreadable case above: here the reader can act, so the guide has to keep asking. The seam left at
Nonekeeps its passing default, so each case turns exactly one setting off.
- tests.test_setup_guide.GATE_EXEMPT_STEPS = {'Enable immutable releases', 'Verify the setup'}¶
Setup steps deliberately left out of the issue’s close gate.
- tests.test_setup_guide.test_setup_guide_requests_administration_permission()[source]¶
The token step asks for Administration, in the form and in the table.
- tests.test_setup_guide.test_setup_guide_missing_administration_is_reported()[source]¶
A PAT without Administration is named in the permission warning table.
- tests.test_setup_guide.OVERSCOPED = (False, "PAT also has access to orchard/cherry. Limit it to this repository under 'Only select repositories'.")¶
What the scope probe answers for a PAT that covers another repository.
- tests.test_setup_guide.test_setup_guide_holds_open_on_a_pat_that_covers_another_repository(scope_ok, expected_has_issues)[source]¶
A PAT that covers another repository reopens the token step, and says why.
The step asks for this repository and nothing else, and nothing read that answer back: the probe ran in
lint-repoalone, as a warning. An undecided probe closes the issue like a pass does, since the reader cannot act on it.Nonekeeps the passing default of the seam.
- tests.test_setup_guide.test_setup_guide_scope_probe_needs_a_pat()[source]¶
Without a PAT there is no token to probe, and the step says nothing of scope.
- tests.test_setup_guide.PACKAGE_PYPROJECT = '[project]\nname = "papaya"\nversion = "1.0.0"\n'¶
A plain PEP 621 project, which uv builds and publishes.
- tests.test_setup_guide.VIRTUAL_PYPROJECT = '[project]\nname = "papaya"\nversion = "1.0.0"\n\n[tool.uv]\npackage = false\n'¶
A uv virtual project: same
[project] name, nothing ever published.
- tests.test_setup_guide.test_setup_guide_pypi_step_gated_on_package_build(tmp_path, monkeypatch, pyproject, step_rendered, expected_has_issues)[source]¶
The Trusted Publisher step follows
is_python_package, notpackage_name.Both projects below declare the same
[project] name, so a gate reading that name alone cannot tell them apart. Only the first one is built and published, and only it should be asked to register a publisher.The lifecycle assertion is the point of the test. With the publisher check failing and every other check passing, the virtual project still closes its issue, where a
package_namegate would hold it open forever: the name is set, so the gate keeps demanding provenance for an upload that no workflow in the repository can ever perform.
- tests.test_setup_guide.test_setup_guide_virtual_project_skips_pypi_probe(tmp_path, monkeypatch)[source]¶
A virtual project never reaches the PyPI API.
The probe is skipped rather than run-and-ignored, so a repository that happens to share its
[project] namewith an unrelated PyPI project is never measured against that stranger’s release.
- tests.test_setup_guide.test_setup_guide_nuitka_disabled_hides_vt_step(tmp_path, monkeypatch)[source]¶
When Nuitka is disabled, the VT step is omitted from the setup guide.
- tests.test_setup_guide.test_setup_guide_vt_step_shown_when_nuitka_active()[source]¶
When Nuitka is active, the VT step appears in the setup guide body.
- tests.test_setup_guide.PAGES_STEP = 'Set GitHub Pages deployment source to GitHub Actions'¶
Title of the step only a GitHub Pages project is asked to perform.
- tests.test_setup_guide.CLOUDFLARE_STEP = 'Configure the Cloudflare Pages credentials'¶
Title of the step only a Cloudflare Pages project is asked to perform.
- tests.test_setup_guide.R2_STEP = 'Configure the R2 upload keys'¶
Title of the step only a site declaring an R2 bucket is asked to perform.
- tests.test_setup_guide.test_setup_guide_asks_about_the_configured_host_only(tmp_path, monkeypatch, target, rendered, omitted)[source]¶
Exactly one deploy step renders, the one
site.deploynames.The two hosts want different things set up and only one of them ever runs, so a guide showing both sends the maintainer to configure a deployment surface nothing publishes to.
- tests.test_setup_guide.test_setup_guide_prefills_a_dated_token_name(tmp_path, monkeypatch)[source]¶
The token form arrives named and dated, with nothing left to type.
Cloudflare’s token list shows what each token can do and never how old it is, so the month has to be carried in the name or a one-year expiry has nothing to be read against.
- tests.test_setup_guide.test_setup_guide_holds_open_until_the_cloudflare_token_lands(tmp_path, monkeypatch, env, expected_has_issues)[source]¶
The token alone closes the step.
Unlike the VirusTotal key, a missing token fails the deploy rather than skipping it, so it gates closure. Nothing else does: the account is derived from the token at run time, so there is no second identifier to demand.
- tests.test_setup_guide.test_setup_guide_asks_for_r2_keys_only_with_a_bucket(tmp_path, monkeypatch, r2)[source]¶
Without a declared bucket, the deploy drops oversized files by design.
- tests.test_setup_guide.test_setup_guide_holds_open_until_the_r2_keys_land(tmp_path, monkeypatch, env, expected_has_issues)[source]¶
A declared bucket without its keys drops the files it exists to serve.
- tests.test_setup_guide.test_setup_guide_asks_a_non_sphinx_site_for_its_cloudflare_credentials(tmp_path, monkeypatch)[source]¶
Declaring the Cloudflare target is the opt-in, Sphinx or not.
A repository whose site is built by its own workflow (a Pelican blog, a hand-rolled static tree) deploys with the same project and the same token, so the guide walks it through them. The GitHub Pages step stays away: repomatic runs no publisher for that combination.
- tests.test_setup_guide.test_setup_guide_closes_with_github_pages_never_configured(tmp_path, monkeypatch)[source]¶
A Cloudflare project closes its issue with no GitHub Pages source set.
check_pages_deployment_sourceanswers404for a repository that never enabled Pages, which the step reads asNoneand collapses to incomplete because it tolerates no unknown. Gated onis_sphinxalone, that wedged the guide open forever on a Cloudflare-hosted project, demanding a deployment source for a host nothing publishes to.
- tests.test_setup_guide.test_every_setup_step_feeds_the_close_gate()[source]¶
Every rendered step decides the issue’s fate, or is exempt on purpose.
A step whose outcome no gate reads renders its ❌ into a body nobody reopens.
sha_pinning_okshipped that way: the guide closed itself with “repository settings complete” while the setting was off, and the only remaining symptom was alint-repowarning nobody reads twice. Asserting over the table means the next step has to be gated or exempted deliberately.
- tests.test_setup_guide.GITHUB_ALERT = re.compile('^\\s*> \\[!(?:NOTE|TIP|IMPORTANT|WARNING|CAUTION)\\]', re.MULTILINE)¶
A GitHub alert marker, which renders as literal text inside
<details>.
- tests.test_setup_guide.test_setup_steps_carry_no_github_alert(name, body)[source]¶
Every step renders inside a
<details>block, where GitHub renders no alert.A
> [!NOTE]there shows its marker as plain text in the issue. A step writes its notes as emoji-labelled quotes instead, like the organization tip.
- tests.test_setup_guide.test_setup_step_placeholders_match_the_template()[source]¶
Every step fills a
$placeholderthe guide template actually declares.A step whose placeholder the template never names renders into nothing, and
string.Template.substituteraises on a template placeholder no step fills, so the two rosters have to agree exactly.