tests.conftest module¶

Shared pytest fixtures and cross-file test helpers.

tests.conftest.PROJECT_ROOT = PosixPath('/home/runner/work/repomatic/repomatic')¶

Root of the repository.

tests.conftest.IN_GIT_CHECKOUT = True¶

Whether the suite runs from a git checkout, not from an sdist or a tag tarball.

A packager’s source carries no git history, holds a release’s frozen workflows, and, for an sdist, a pyproject.toml rewritten and symlinks dereferenced by the build backend. The tests marked repo_maintenance check those properties of a development checkout, so pytest_collection_modifyitems() skips them everywhere else.

tests.conftest.PACKAGE_DIR = PosixPath('/home/runner/work/repomatic/repomatic/repomatic')¶

Root of the package whose sources the conformance tests scan.

tests.conftest.PACKAGE_FILES = [PosixPath('/home/runner/work/repomatic/repomatic/repomatic/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/__main__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/awesome_toc.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/broken_links.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cache.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/changelog.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cli/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cli/github.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cli/lint.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cli/main.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cli/release.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cli/sample.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cli/setup.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cli/sync.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cloudflare.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/cloudflare_r2.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/compat.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/config.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/data/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/data/awesome_template/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/deps/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/deps/dep_graph.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/deps/dep_policy.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/deps/dep_report.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/deps/dep_sources.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/deps/uv.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/deps/vulnerable_deps.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/docs.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/file_inventory.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/file_ops.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/forge.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/frontmatter.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/git_ops.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/actions.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/ci_status.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/dev_release.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/gh.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/issue.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/job_timings.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/matrix.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/pr.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/pr_body.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/release_sync.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/releases.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/sponsor.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/status.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/token.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/unsubscribe.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/github/workflow_sync.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/gitignore.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/hashing.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/http.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/humanize.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/images.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/init_project.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/labels.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/lint_repo.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/mailmap.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/matrix_axes.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/metadata/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/metadata/core.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/metadata/env.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/metadata/git.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/metadata/matrix.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/metadata/project.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/metric_chart.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/metrics.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/npm.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/pages_redirects.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/pypi.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/pyproject.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/registry.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/release/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/release/attestation.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/release/binaries_page.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/release/binary.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/release/checksums.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/release/prepare_release.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/release/version_sync.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/release/virustotal.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/runner_catalog.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/runner_images.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/setup_guide.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/sync_ops.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/tabular.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/templates/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/tooling/__init__.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/tooling/bundle.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/tooling/plugin.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/tooling/tool_registry.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/tooling/tool_runner.py'), PosixPath('/home/runner/work/repomatic/repomatic/repomatic/versions.py')]¶

Every Python module shipped in the package.

Sorted because @pytest.mark.parametrize derives test IDs from iteration order, and pytest-xdist aborts a run whose workers disagree on them.

tests.conftest.SITE_ANCHOR_RE = re.compile('https://repomatic\\.net/(?P<page>[\\w-]+)#(?P<anchor>[\\w-]+)')¶

A link into one section of the published documentation.

tests.conftest.WORKFLOWS_DIR = PosixPath('/home/runner/work/repomatic/repomatic/.github/workflows')¶

Directory holding this repository’s own workflow files.

tests.conftest.docs_heading_slug(title)[source]¶

The anchor the published Sphinx site derives from a heading.

Follows the docutils section-id algorithm (lowercase, every non-alphanumeric run collapsed to one hyphen, trimmed), which is what the published page exposes as id= attributes.

Parameters:

title (str) – Heading text, without its leading # marks.

Return type:

str

tests.conftest.docs_heading_anchors(page)[source]¶

Anchors a page of docs/ exposes: heading slugs and explicit targets.

Heading slugs follow docs_heading_slug().

Parameters:

page (str) – Name of the page, without its .md extension.

Return type:

set[str]

tests.conftest.assert_shipped_text_reads_true_downstream(origin, text)[source]¶

Check a text that travels downstream cites nothing a repository lacks.

A bundled configuration and a generated workflow comment both land in repositories that hold no copy of this project’s claude.md: the agent component that shipped its sections retired in 7.14.0. A pointer to that file is a dead end there. So such a text states its rationale inline, or cites a section of the published documentation, whose anchor is held here to a heading that exists.

Parameters:
  • origin (str) – Name of the text under test, for the failure message.

  • text (str) – Content as it ships.

Return type:

None

tests.conftest.pytest_configure(config)[source]¶

Register the marker local to this suite.

Not in [tool.pytest] markers, which must match the template bundled in repomatic/data/pytest.toml.

Return type:

None

tests.conftest.pytest_collection_modifyitems(config, items)[source]¶

Skip the repo_maintenance tests outside a git checkout.

See IN_GIT_CHECKOUT.

Return type:

None

tests.conftest.WORKFLOWS_WITH_CONCURRENCY_BLOCK = ('autofix.yaml', 'changelog.yaml', 'debug.yaml', 'docs.yaml', 'labels.yaml', 'lint.yaml', 'metrics.yaml', 'release.yaml', 'self-maintenance.yaml', 'tests.yaml')¶

Workflows that actually define a concurrency block, read off disk.

Derived rather than hand-listed because two test modules assert against it from opposite directions, and a hand-listed pair drifted apart once already: a workflow can be exempt from the requirement and still carry a block, which no list of intentions can express.

tests.conftest.WORKFLOWS_WITHOUT_CONCURRENCY_BLOCK = ('_release-build.yaml', '_release-engine.yaml', 'autolock.yaml', 'cancel-runs.yaml', 'unsubscribe.yaml')¶

The complement of WORKFLOWS_WITH_CONCURRENCY_BLOCK.

tests.conftest.skip_unless_tool_runs(name)[source]¶

Skip the calling test when a registry tool cannot run on this machine.

A test that lints generated output reads the tool’s verdict off its exit code, and a uvx resolution that fails offline exits non-zero too. Probing with --version first keeps the two apart, so whatever the real invocation returns afterwards is the tool’s opinion of the content rather than a fetch failure misread as a defect in the generator.

Only a developer with no network ever takes the skip: CI is the enforcement point, and a tool it cannot fetch fails the job long before this runs.

Parameters:

name (str) – Registry key of the tool the caller is about to invoke.

Return type:

None

tests.conftest.load_workflow(workflow_name)[source]¶

Load and parse one of this repository’s workflow YAML files.

The one loader behind every test that reads a workflow off disk, so they all parse the same bytes the same way.

Return type:

dict[str, Any]

tests.conftest.hermetic_git(monkeypatch)[source]¶

Shield temporary git repos from the developer’s own git configuration.

Commit signing, hooks, templates and identity from the global or system config would otherwise leak into repos a test builds under tmp_path and break hermeticity in machine-specific ways.

class tests.conftest.FakeResponse(data)[source]¶

Bases: object

Minimal urlopen response double: a byte body behind a context manager.

Stands in for the object repomatic.http.get_json reads, so network tests patch repomatic.http.urlopen with return_value=FakeResponse(...).

read()[source]¶
Return type:

bytes

tests.conftest.pat_results(**overrides)[source]¶

Build a PatPermissionResults where every check passes by default.

Each keyword names a PatPermissionResults field and replaces its (passed, message) tuple, so a test spells out only the checks whose outcome it cares about and the rest read as the happy path.

Parameters:

overrides (tuple[bool, str]) – Field name to its (passed, message) result.

Return type:

PatPermissionResults

Returns:

The assembled results object.

tests.conftest.metadata_from_pyproject(tmp_path, monkeypatch, content)[source]¶

Build a Metadata reading a throwaway pyproject.toml holding content.

Repointing Metadata.pyproject_path is what isolates the instance from the repository’s own pyproject.toml: the class reads that path lazily, so the patch has to be in place before the first property is touched. The autouse _reset_metadata fixture clears the cached values afterwards.

Parameters:
  • tmp_path (Path) – Directory to write the file into.

  • monkeypatch – The test’s monkeypatch fixture.

  • content (str) – Full pyproject.toml source.

Return type:

Metadata

Returns:

A Metadata bound to the written file.

tests.conftest.cache_env(monkeypatch, tmp_path)[source]¶

Point the repomatic cache at a per-test directory, auto-purge disabled.

Returns the cache root path so tests can assert on its contents.

tests.conftest.patch_gh(module, **mock_kwargs)[source]¶

Patch a module’s gh dispatch points with one shared mock.

Read paths go through gh_api_json (which calls the gh module’s run_gh_command) while write paths call the name imported into module directly; patching both with the same mock keeps a test’s single call sequence intact across the two layers. Bind the module with functools.partial for a per-file shorthand.

tests.conftest.attribute_docstrings(body)[source]¶

Yield (line, owner, text) for each attribute docstring in body.

An attribute docstring is the bare string literal following an assignment. ast.get_docstring cannot see one, yet it is this project’s convention for documenting a dataclass field or a module constant, and automodule ... :undoc-members: renders it.