tests.test_pr_body moduleΒΆ

Tests for PR body generation.

tests.test_pr_body.github_env(monkeypatch)[source]ΒΆ

Populate the full GITHUB_* environment a workflow run would provide.

Metadata reads these lazily, so every test that renders a metadata block or a refresh tip needs the whole set in place before it builds one.

tests.test_pr_body.test_extract_workflow_filename(workflow_ref, expected)[source]ΒΆ

Extract workflow filename from various reference formats.

tests.test_pr_body.test_demote_markdown_headings(text, expected)[source]ΒΆ

Headings are uniformly demoted so the shallowest lands on the floor.

tests.test_pr_body.test_unescape_dollars(text, expected)[source]ΒΆ

\$ sequences are converted back to $.

tests.test_pr_body.test_parse_template_unescapes_body()[source]ΒΆ

Body \$ placeholders are unescaped at parse time.

tests.test_pr_body.test_parse_template_unescapes_no_frontmatter()[source]ΒΆ

Templates without frontmatter also get \$ unescaped.

footer: false opts out whether written as a YAML boolean or a string.

The frontmatter is real YAML, so a bare false arrives as a boolean, but a downstream template may have quoted it. Both spell the same intent.

tests.test_pr_body.test_generate_metadata_block_all_vars(github_env)[source]ΒΆ

Metadata block includes all expected fields when env vars are set.

tests.test_pr_body.test_generate_metadata_block_rerun(github_env, monkeypatch)[source]ΒΆ

Re-run by entry appears when triggering actor differs from actor.

tests.test_pr_body.test_generate_metadata_block_docs_entry(github_env)[source]ΒΆ

The documentation deep link leads the list when a template provides one.

tests.test_pr_body.test_generate_metadata_block_minimal_vars(monkeypatch)[source]ΒΆ

Graceful degradation when most env vars are unset.

tests.test_pr_body.test_generate_refresh_tip_with_workflow_ref(github_env)[source]ΒΆ

Tip includes workflow dispatch URL when env vars are set.

tests.test_pr_body.test_generate_refresh_tip_without_workflow_ref(monkeypatch)[source]ΒΆ

Tip is empty when workflow ref is unavailable.

tests.test_pr_body.test_get_template_names()[source]ΒΆ

All expected template names are discovered.

tests.test_pr_body.test_load_template_frontmatter()[source]ΒΆ

Frontmatter is parsed correctly from a parameterized template.

tests.test_pr_body.test_load_template_title_only_frontmatter()[source]ΒΆ

Static templates have frontmatter with title but no args.

tests.test_pr_body.test_load_template_external_file(tmp_path)[source]ΒΆ

A Path argument loads the template from disk.

tests.test_pr_body.test_load_template_external_file_missing(tmp_path)[source]ΒΆ

Pointing at a non-existent file raises FileNotFoundError.

tests.test_pr_body.test_render_template_external_file(tmp_path)[source]ΒΆ

External templates render with substitution like packaged ones.

tests.test_pr_body.test_render_title_no_title_returns_empty(tmp_path)[source]ΒΆ

A template without a title field renders to an empty string.

tests.test_pr_body.test_render_commit_message_no_title_returns_empty(tmp_path)[source]ΒΆ

Templates with neither commit_message nor title render empty.

tests.test_pr_body.test_template_args_parameterized()[source]ΒΆ

Parameterized templates report their required arguments.

tests.test_pr_body.test_template_args_static()[source]ΒΆ

Static templates report no required arguments.

tests.test_pr_body.test_render_title_static()[source]ΒΆ

Static templates have a literal title.

tests.test_pr_body.test_render_title_parameterized()[source]ΒΆ

Parameterized templates substitute variables in the title.

tests.test_pr_body.test_render_commit_message_falls_back_to_title()[source]ΒΆ

Templates without explicit commit_message fall back to title.

tests.test_pr_body.test_render_commit_message_explicit()[source]ΒΆ

Templates without explicit commit_message fall back to title with backticks.

tests.test_pr_body.test_render_commit_message_parameterized()[source]ΒΆ

Parameterized templates substitute variables in commit_message.

The bump-version message must start with a MANUAL_VERSION_BUMP_COMMIT_PREFIXES member: that is the emission half of the invariant the workflow gates match against (see test_changelog_prefix_is_the_machinery_invariant).

tests.test_pr_body.test_render_bump_version()[source]ΒΆ

Bump version template includes part, version, and merge instructions.

tests.test_pr_body.test_render_prepare_release()[source]ΒΆ

Prepare release template includes version, links, and caution admonition.

tests.test_pr_body.test_render_prepare_release_without_review_steps()[source]ΒΆ

Empty review fragments degrade the checklist to the merge instructions.

tests.test_pr_body.test_build_release_review_steps(github_env, monkeypatch)[source]ΒΆ

Review steps embed the draft dev URL and the previous-version compare link.

tests.test_pr_body.test_build_release_review_steps_partial(github_env, monkeypatch)[source]ΒΆ

Each step is omitted independently when its release data is missing.

tests.test_pr_body.test_build_release_review_steps_unavailable(github_env, monkeypatch)[source]ΒΆ

Both steps collapse to empty strings when the lookup returns nothing.

tests.test_pr_body.test_render_surfaces_the_config_surface(template, needles)[source]ΒΆ

Each job’s PR body points the reader at the knobs that job obeys.

tests.test_pr_body.test_render_upgrade_title()[source]ΒΆ

The upgrade pull request is titled after the release it adopts.

tests.test_pr_body.test_render_upgrade_invite()[source]ΒΆ

The invitation names both releases and hands over the launcher verbatim.

tests.test_pr_body.VALID_CONFIG_KEYS = frozenset({'abandoned-versions', 'action-pins.sync', 'awesome-template.sync', 'binaries.sync', 'bumpversion.sync', 'cache.dir', 'cache.github-release-ttl', 'cache.github-releases-ttl', 'cache.max-age', 'cache.npm-ttl', 'cache.pypi-ttl', 'changelog.archive-location', 'changelog.bullet-word-threshold', 'changelog.location', 'debug.sync', 'dep-sources.sync', 'dependency-graph.all-extras', 'dependency-graph.all-groups', 'dependency-graph.level', 'dependency-graph.no-extras', 'dependency-graph.no-groups', 'dependency-graph.output', 'dependency-graph.update', 'dev-release.sync', 'docs.apidoc-exclude', 'docs.apidoc-extra-args', 'docs.update-script', 'exclude', 'flavor.agent', 'flavor.ci', 'gitignore.extra-categories', 'gitignore.extra-content', 'gitignore.location', 'gitignore.sync', 'include', 'labels.content-rules', 'labels.extra', 'labels.extra-files', 'labels.file-rules', 'labels.sync', 'lint-deps.allow', 'lint-deps.comment-word-threshold', 'lint-repo.declined', 'mailmap.sync', 'manpages.asset-name', 'manpages.script', 'metrics.charts', 'metrics.colors', 'metrics.forges', 'metrics.predecessors', 'metrics.skip', 'metrics.store', 'metrics.subjects', 'metrics.sync', 'minimum-release-age', 'notification.max-unsubscribes', 'notification.months', 'notification.unsubscribe', 'nuitka.dev-targets', 'nuitka.enabled', 'nuitka.entry-points', 'nuitka.extras', 'nuitka.nofollow-imports', 'nuitka.unstable-targets', 'pypi-package-history', 'release-assets', 'settings.location', 'setup-guide', 'site.cloudflare-compatibility-date', 'site.cloudflare-placement', 'site.cloudflare-project', 'site.cloudflare-r2-bucket', 'site.cloudflare-r2-domain', 'site.deploy', 'skills.location', 'sphinx.builder', 'subagents.location', 'sync-runner-images.ignore', 'test-matrix.exclude', 'test-matrix.full-include', 'test-matrix.include', 'test-matrix.remove', 'test-matrix.replace', 'test-matrix.unstable', 'test-matrix.variations', 'tool-versions.sync', 'upstream-pin.sync', 'uv-lock.sync', 'vulnerable-deps.sources', 'vulnerable-deps.sync', 'workflow-pins.sync', 'workflow.extra-paths', 'workflow.ignore-paths', 'workflow.paths', 'workflow.source-paths', 'workflow.sync'})ΒΆ

Every [tool.repomatic] key, dotted and nested-expanded, from the schema.

tests.test_pr_body.test_template_config_options_are_real_keys(name)[source]ΒΆ

Every option a template lists is a real key, anchored and sorted.

Each - `key <…/configuration#anchor>`_ bullet must name a live [tool.repomatic] key, link to its matching anchor, and appear in alphabetical order. Guards against a renamed or mistyped option silently surviving in a PR body template (the lone unenforced workflow.sync was one such drift).

Return type:

None

tests.test_pr_body.SWITCH_KEY_RE = re.compile('exit_if_disabled\\(.*?"(?P<key>[\\w.-]+)",?\\s*\\)', re.DOTALL)ΒΆ

The [tool.repomatic] key a command hands to exit_if_disabled.

tests.test_pr_body.test_templates_list_the_switch_of_their_command()[source]ΒΆ

A pull request body that lists options names the one that stops it.

A command gated by exit_if_disabled opens no pull request once its switch is off, so the option list of that pull request is where a maintainer looks for the switch. Only a template named after its command is read: audit and sync-binaries fill a template of another name. A template with no option list is left alone, like the setup-guide issue body.

Return type:

None

Simulates generate_pr_metadata_block(Metadata()) output for unit tests.

tests.test_pr_body.test_build_pr_body_with_prefix(monkeypatch)[source]ΒΆ

Prefix is prepended with triple newline separator.

tests.test_pr_body.test_build_pr_body_with_tip(github_env)[source]ΒΆ

Tip is inserted between prefix and footer when env vars are set.

tests.test_pr_body.test_build_pr_body_empty_prefix(monkeypatch)[source]ΒΆ

Empty prefix without tip still includes footer and metadata.

tests.test_pr_body.test_build_pr_body_trims_oversized_prefix(github_env)[source]ΒΆ

An oversized prefix is trimmed so the tail always survives.

GitHub and create-pull-request truncate oversized bodies from the end, which silently drops the refresh tip, metadata block, and attribution footer (the fate of huge sync-uv-lock tables). The prefix is trimmed instead, on line boundaries, with a caution admonition marking the cut.

tests.test_pr_body.test_build_pr_body_under_limit_untouched(github_env)[source]ΒΆ

A body under the limit is returned without any truncation notice.

An oversized issue body is trimmed above the footer, which survives.

Unlike PR bodies (silently truncated by create-pull-request), oversized issue bodies make gh issue create and gh issue edit fail outright, so the guard rewrites the body to fit before it reaches the API.

tests.test_pr_body.test_fit_github_body_small_unchanged()[source]ΒΆ

A body under the limit passes through byte-for-byte.

A footerless oversized body is trimmed too, without inventing a footer.

tests.test_pr_body.REFERENCE_WORKFLOWS = ('.github/workflows/autofix.yaml', '.github/workflows/changelog.yaml', '.github/workflows/_release-build.yaml', '.github/workflows/_release-engine.yaml', '.github/workflows/metrics.yaml', '.github/workflows/self-maintenance.yaml')ΒΆ

Workflow files that reference PR body templates via --template.

tests.test_pr_body.PROGRAMMATIC_TEMPLATES = frozenset({'available-admonition', 'broken-links-issue', 'development-warning', 'generated-footer', 'github-releases', 'immutable-releases', 'pr-metadata', 'refresh-tip', 'release-notes', 'release-sync-report', 'setup-guide', 'setup-guide-branch-ruleset', 'setup-guide-cloudflare-pages', 'setup-guide-cloudflare-r2', 'setup-guide-dependabot', 'setup-guide-fork-pr-approval', 'setup-guide-notifications-pat', 'setup-guide-pages-source', 'setup-guide-pypi-trusted-publisher', 'setup-guide-sha-pinning-required', 'setup-guide-token', 'setup-guide-verify', 'setup-guide-virustotal', 'unavailable-admonition', 'unsubscribe-phase1', 'unsubscribe-phase2', 'upgrade-invite', 'yanked-admonition'})ΒΆ

Templates rendered from Python code, not via the --template CLI flag.

tests.test_pr_body.WORKFLOWS_DOCS_URL = 'https://repomatic.net/workflows'ΒΆ

Hosted workflows reference that template docs: fields deep-link into.

tests.test_pr_body.test_template_file_policy(filename, name)[source]ΒΆ

Each PR template file must be a valid .md file with correct frontmatter.

tests.test_pr_body.FRONTMATTER_KEY_ORDER = ['args', 'title', 'commit_message', 'docs', 'footer']ΒΆ

Canonical ordering of keys within template frontmatter.

tests.test_pr_body.test_frontmatter_key_ordering(filename, name)[source]ΒΆ

Frontmatter keys must follow the canonical order in FRONTMATTER_KEY_ORDER.

yaml.safe_load preserves document order in the mapping it builds, so the parsed keys read back in the order the template spells them.

tests.test_pr_body.test_version_dev_suffix_stripping(version, expected)[source]ΒΆ

The .devN suffix is stripped from auto-detected versions.

Exercises the shared helper both pr-body --version auto and repomatic init’s default pin route through, rather than restating its regex here: a test that spells the pattern itself passes no matter what the callers do.

tests.test_pr_body.test_templates_match_workflow_references()[source]ΒΆ

Every CLI template must be referenced by --template in a workflow file.

tests.test_pr_body.test_cli_template_file(tmp_path, monkeypatch)[source]ΒΆ

--template-file renders a markdown body from an external template.

tests.test_pr_body.test_cli_prefix_file_reads_the_prefix_from_disk(tmp_path, monkeypatch)[source]ΒΆ

--prefix-file carries a report too large to pass through the env.

tests.test_pr_body.test_cli_prefix_file_wins_over_prefix(tmp_path, monkeypatch)[source]ΒΆ

With both given, the file is the one the workflow means.

tests.test_pr_body.test_cli_template_and_template_file_mutually_exclusive(tmp_path, monkeypatch)[source]ΒΆ

Passing both --template and --template-file is rejected.

tests.test_pr_body.test_cli_template_arg_invalid_format(tmp_path, monkeypatch)[source]ΒΆ

--template-arg without = is rejected with a clear error.

tests.test_pr_body.test_cli_template_arg_overrides_dedicated_flag(tmp_path, monkeypatch)[source]ΒΆ

--template-arg values supersede the dedicated --version flag.

tests.test_pr_body.test_template_commit_message_carries_no_ci_skip_token(name)[source]ΒΆ

No generated commit message may silently skip CI.

Every sync-*, format-* and fix-* job commits a message rendered from a template, in this repository and in every downstream one. A skip token anywhere in that message stops the run, and because the required check is then never reported, the pull request blocks on a β€œPending” status instead of failing visibly.

Return type:

None

tests.test_pr_body.test_template_commit_subject_claims_no_bracket_prefix(name)[source]ΒΆ

A [bracketed] prefix is reserved for machine-parsed mechanisms.

The one family templates may emit is repomatic.git_ops.CHANGELOG_COMMIT_PREFIX, which repomatic.git_ops.VERSION_BUMP_COMMIT_PREFIXES matches back (bump-version is the template that earns it). Any other bracket would either collide with that matcher or invent a label nothing reads. See docs/commit-messages.md.

Return type:

None

tests.test_pr_body.test_only_the_changelog_prefix_is_bracketed()[source]ΒΆ

The parsed-prefix set stays a single bracket family.

docs/commit-messages.md tells readers that [changelog] is the only bracket prefix any machine reads, carried by every version-machinery commit. This pins that claim to the code, so a second bracket namespace fails here and forces the documentation to follow.

Return type:

None