tests.test_tool_runner module¶
Tests for the unified tool runner.
- tests.test_tool_runner.test_tool_spec_integrity(name, spec)[source]¶
Each registry entry passes structural integrity checks.
- tests.test_tool_runner.test_bundled_actionlint_labels_name_runners_the_matrix_emits()[source]¶
Every declared label answers a runner the generated workflows can name.
actionlint validates
runs-on:against a list baked into its binary, and rejects a GitHub-hosted label its release predates. The bundled config declares those throughself-hosted-runner, actionlint’s escape hatch for an unrecognized label, which is why the entries carry no implication of being self-hosted.Pinning them to the matrix constants is what keeps the file from rotting: an entry surviving a move to a newer image would go on whitelisting a runner nothing uses, and read as still load-bearing.
- tests.test_tool_runner.test_tool_backend_labels_unique()[source]¶
Every backend resolves distinct display labels for the docs tables.
- tests.test_tool_runner.test_tool_backend_matches_payload_fields(name, spec)[source]¶
Each registry entry maps onto exactly one delivery backend.
- tests.test_tool_runner.test_build_install_args_cooldown(name, spec, tmp_path, monkeypatch)[source]¶
uvx tools carry
--exclude-newer; needs_venv tools only when unlocked.
- tests.test_tool_runner.test_uvx_tool_pins_registry_version(name, spec, tmp_path, monkeypatch)[source]¶
Every uvx-installed tool pins its
TOOL_REGISTRYversion in the command.Locks the invariant that keeps the per-tool command tests drift-proof: a version bump in
TOOL_REGISTRYmust flow into thename==versionpin with no hand-edited literal. A hard-codedname==X.Y.Zin an individual test silently goes stale on the next bump (a missedpyproject-fmtliteral once reddened every Tests cell), so version-pinning is asserted here once, generically, against the shared builder rather than restated per tool.The pin sits in the command that installs the tool. With a
uv.lock, a tool declaringinstall_into_venvis installed by its own command.
- tests.test_tool_runner.test_build_install_args_install_into_venv(name, spec, tmp_path, monkeypatch)[source]¶
A tool installed into the project venv runs from it, with no layer.
- tests.test_tool_runner.test_install_into_venv_installs_the_pin(mock_run, name, spec)[source]¶
The pinned tool goes into the venv that
uv run --frozenreports.
- tests.test_tool_runner.test_install_into_venv_reports_a_uv_failure(mock_run, results, message)[source]¶
A uv failure stops the run before the tool starts.
- tests.test_tool_runner.test_datasource_url_by_backend()[source]¶
datasource_url points at npmjs for npm tools, GitHub/PyPI otherwise.
- tests.test_tool_runner.test_install_npm_command_shape(mock_which, mock_run, tmp_path)[source]¶
The npm backend installs pkg@version into a prefix with the cooldown flag.
- tests.test_tool_runner.test_install_npm_zero_cooldown_omits_flag(mock_which, mock_run, tmp_path)[source]¶
A 0-day cooldown drops the min-release-age flag entirely.
- tests.test_tool_runner.test_install_npm_without_npm_raises(mock_which, tmp_path)[source]¶
A clear error surfaces when Node.js and npm are not on PATH.
- tests.test_tool_runner.test_npm_supports_cooldown(mock_run, version_output, supported)[source]¶
min-release-age support is gated on npm >= 11.10.0.
- tests.test_tool_runner.test_install_npm_warns_when_npm_too_old(mock_which, mock_run, tmp_path, caplog)[source]¶
Old npm cannot enforce the cooldown, so the runner warns but still installs.
- tests.test_tool_runner.test_tool_registry_sorted_alphabetically()[source]¶
Registry keys are sorted alphabetically.
- tests.test_tool_runner.test_pypi_name_strips_install_extra()[source]¶
pypi_nameyields the bare PyPI project name, dropping any install extra.Nuitka pins
package="nuitka[onefile]"for installation, but the PyPI JSON API 404s on the bracketed name, sopypi_namemust returnnuitkaforsync-tool-versionsto find the real project.
- tests.test_tool_runner.test_pypi_pin_names_the_package_and_its_version(package, pin)[source]¶
pypi_pinis the exact requirement an installer takes, extra included.The command tests above read the pin from this property, so its shape is stated here once.
- tests.test_tool_runner.test_checksum_sidecar_covers_exactly_binary_tools()[source]¶
CHECKSUMSandVERSIONSare keyed by exactly the binary tool names.
- tests.test_tool_runner.test_checksum_version_matches_registry(name)[source]¶
Each tool’s sidecar version stamp matches its
ToolSpec.version.The offline tripwire: a version bump that did not refresh the checksums leaves
VERSIONSstale, failing here before the release is cut. Fix by runningrepomatic update-checksums.
- tests.test_tool_runner.test_resolve_platform_exact_match()[source]¶
Exact Platform match takes priority over Group membership.
- tests.test_tool_runner.test_resolve_platform_group_match()[source]¶
Group membership matches when no exact Platform key exists.
- tests.test_tool_runner.test_resolve_platform_no_match()[source]¶
No matching key raises RuntimeError.
- tests.test_tool_runner.test_resolve_platform_unknown_distro_falls_back_to_linux()[source]¶
An unidentified distribution still reaches the family-wide
LINUXkey.extra-platforms returns
UNKNOWN_PLATFORMfor any distribution it has yet to learn, and that sentinel belongs to no group, so the group pass misses. The manylinux_2_28 build container (AlmaLinux) is the motivating case: it broke everyrepomatic runbinary on a distro identity that never entered the choice of binary.
- tests.test_tool_runner.test_resolve_platform_unknown_distro_refuses_beyond_linux(urls, sys_platform)[source]¶
The fallback widens to the Linux family and no further.
- tests.test_tool_runner.test_linux_binaries_are_keyed_on_the_family_group()[source]¶
Every tool shipping a Linux binary keys it on the
LINUXgroup.The pass-3 fallback only rescues a family-wide key, so a tool keyed on a specific distribution instead would still fail inside the manylinux build container.
- tests.test_tool_runner.test_platform_cache_key()[source]¶
Cache key is a filesystem-safe string derived from the PlatformKey.
- tests.test_tool_runner.test_download_and_verify_success(tmp_path)[source]¶
Successful download with matching checksum writes the file.
- tests.test_tool_runner.test_download_and_verify_no_content_length(tmp_path)[source]¶
A response without Content-Length downloads via the indeterminate Spinner.
- tests.test_tool_runner.test_download_and_verify_mismatch(tmp_path)[source]¶
Checksum mismatch raises ValueError and cleans up.
- tests.test_tool_runner.test_download_and_verify_retries_transient_failure(tmp_path)[source]¶
A transient network failure is retried and the next attempt succeeds.
- tests.test_tool_runner.test_download_and_verify_gives_up_after_attempts(tmp_path)[source]¶
A persistent network failure surfaces after the last attempt.
- tests.test_tool_runner.test_download_and_verify_truncated(tmp_path)[source]¶
A body shorter than Content-Length raises OSError, not a mismatch.
A truncated transfer (proxy hiccup, dropped connection) hashes to a wrong digest, so it used to be reported as a SHA-256 mismatch: that reads as a stale pin or a tampered artifact when the registry checksum is correct. Truncation is retried like any network failure, so the error only surfaces once every attempt came up short.
- tests.test_tool_runner.test_extract_binary_raw(tmp_path)[source]¶
RAW format renames the archive to the executable name.
- tests.test_tool_runner.test_extract_binary_tar_gz(tmp_path)[source]¶
TAR_GZ extracts the named executable and makes it executable.
- tests.test_tool_runner.test_extract_binary_tar_gz_with_strip_components(tmp_path)[source]¶
TAR_GZ with strip_components strips leading path components.
- tests.test_tool_runner.test_extract_binary_tar_xz(tmp_path)[source]¶
TAR_XZ extracts the named executable.
- tests.test_tool_runner.test_extract_binary_tar_missing_executable(tmp_path)[source]¶
Missing executable in tar archive raises FileNotFoundError.
- tests.test_tool_runner.test_extract_binary_tar_unsafe_path(tmp_path)[source]¶
Archive member with path traversal raises ValueError.
- tests.test_tool_runner.test_extract_binary_zip(tmp_path)[source]¶
ZIP format extracts the named executable and makes it executable.
- tests.test_tool_runner.test_extract_binary_zip_with_strip_components(tmp_path)[source]¶
ZIP with strip_components strips leading path components.
- tests.test_tool_runner.test_extract_binary_zip_missing_executable(tmp_path)[source]¶
Missing executable in ZIP archive raises FileNotFoundError.
- tests.test_tool_runner.test_extract_binary_zip_unsafe_path(tmp_path)[source]¶
ZIP member with path traversal raises ValueError.
- tests.test_tool_runner.test_extract_binary_format_override(tmp_path)[source]¶
Per-platform archive format from dict is used when passed.
- tests.test_tool_runner.test_get_archive_format_dict_resolution()[source]¶
Dict archive_format resolves Platform > Group membership.
- tests.test_tool_runner.test_install_binary_missing_platform()[source]¶
Missing platform key in binary spec raises RuntimeError.
- tests.test_tool_runner.test_path_tools_env_skips_platform_without_binary(caplog)[source]¶
A companion publishing no binary here is skipped, not fatal.
shfmtships nothing for Windows ARM64, so provisioning it for mdformat can never succeed there. Aborting would makerepomatic run mdformatunusable on the platform rather than merely leaving its shell blocks unformatted, so the run carries on withPATHuntouched.
- tests.test_tool_runner.TARBALL_URL = 'https://example.com/{version}/tool.tar.gz'¶
Download URL for the tests that go through the extract step.
- tests.test_tool_runner.test_install_binary_cache_hit(tmp_path, monkeypatch, cache_env)[source]¶
_install_binary returns cached path when cache hit and sidecar matches.
- tests.test_tool_runner.test_install_binary_cache_hit_on_a_nested_archive_executable(tmp_path, monkeypatch, cache_env, cached_name)[source]¶
A cached entry is found whatever path the executable held in the archive.
archive_executablenames a location inside the archive, while the cache keys an entry on the extracted file’s own name. Probing with the nested path matched nothing, so a tool declaring one re-downloaded on every call despite a complete, sidecar-verified copy sitting in the cache.gh(bin/gh) was the only registry tool to declare one, and since everyghinvocation routes through the pinned binary, it re-fetched 13 MB per run.
- tests.test_tool_runner.test_install_binary_cache_miss_stores(tmp_path, monkeypatch, cache_env)[source]¶
_install_binary stores the binary in cache after download on miss.
- tests.test_tool_runner.test_install_binary_no_cache_flag(tmp_path, monkeypatch, cache_env)[source]¶
_install_binary with no_cache=True bypasses cache entirely.
- tests.test_tool_runner.test_install_binary_cache_integrity_failure(tmp_path, monkeypatch, cache_env)[source]¶
_install_binary re-downloads when cached binary fails sidecar check.
- tests.test_tool_runner.test_install_binary_cache_store_fallback(tmp_path, monkeypatch, cache_env)[source]¶
_install_binary falls back to temp path when cached file is missing.
- tests.test_tool_runner.test_run_tool_binary_uses_direct_path(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
Binary tools use the downloaded binary path, not uvx.
- tests.test_tool_runner.test_run_tool_holds_excludes_for_explicit_paths(mock_ci, mock_install, mock_run, tmp_path, monkeypatch, name, flag, extra_args)[source]¶
A path named on the command line stays subject to the tool’s exclude list.
ruff and typos apply their exclude list only while they walk a directory, so a file passed by name is processed whatever the list says, unless the flag that holds the list comes with it.
- tests.test_tool_runner.test_run_tool_binary_forwards_extra_args(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
Extra args are appended after default flags for binary tools.
- tests.test_tool_runner.test_run_tool_binary_default_flags(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
Binary tools include default_flags in the command.
- tests.test_tool_runner.test_run_tool_ruff_bundled_default(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
ruff uses bundled default config when no config exists.
- tests.test_tool_runner.test_run_tool_ruff_reads_pyproject_natively(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
ruff gets no config file when [tool.ruff] exists in pyproject.toml.
- tests.test_tool_runner.test_run_tool_bump_my_version_via_uvx(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
bump-my-version runs via uvx with subcommand extra_args.
- tests.test_tool_runner.test_ensure_binary_memoizes_per_tool(mock_install, tmp_path)[source]¶
Repeated calls install once and return the same path.
Callers in a loop (
format-imagesoptimizing one PNG per call) must not pay the install-and-verify path once per file.
- tests.test_tool_runner.test_ensure_binary_cleans_staging_on_cache_hit(mock_install, tmp_path)[source]¶
When the install lands in the cache, the staging directory is removed.
- tests.test_tool_runner.test_ensure_binary_keeps_staging_fallback_alive(mock_install)[source]¶
A staging-path fallback survives the call instead of dangling.
When the cache store fails (unwritable root, Docker overlay losing the write),
_install_binaryreturns the staging copy: the staging directory must then outlive the call, or the caller would exec a just-deleted file.
- tests.test_tool_runner.test_resolve_config_reads_pyproject_with_section()[source]¶
Tools with reads_pyproject=True skip translation when [tool.X] exists.
- tests.test_tool_runner.test_resolve_config_reads_pyproject_falls_through_to_bundled(tmp_path, monkeypatch, cache_env)[source]¶
Tools with reads_pyproject=True use bundled default when no config exists.
- tests.test_tool_runner.test_resolve_config_native_file_wins(tmp_path, monkeypatch)[source]¶
Native config file takes precedence over everything else.
- tests.test_tool_runner.test_resolve_config_pyproject_section(tmp_path, monkeypatch, cache_env)[source]¶
[tool.X] in pyproject.toml produces a cached config file.
- tests.test_tool_runner.test_resolve_config_toml_translation(tmp_path, monkeypatch, cache_env)[source]¶
[tool.X] with native_format=’toml’ produces a valid TOML cached file.
- tests.test_tool_runner.test_resolve_config_toml_nested_tables(tmp_path, monkeypatch, cache_env)[source]¶
Nested dicts in [tool.X] produce TOML table sections.
- tests.test_tool_runner.test_resolve_config_toml_preserves_pyproject_comments(tmp_path, monkeypatch)[source]¶
A [tool.X] TOML section materialised to a native file keeps its comments.
Reading the section live (not via a plain-dict copy) lets the generated standalone file carry the user’s comments while dropping the
[tool.X]prefix.
- tests.test_tool_runner.test_native_format_toml_serialize_reroots_section_and_keeps_comments()[source]¶
A live [tool.X] section loses its prefix and keeps every comment.
- tests.test_tool_runner.test_native_format_toml_serialize_inline_section()[source]¶
An inline
tool.x = {...}table serializes to the same data.
- tests.test_tool_runner.test_resolve_config_json_translation(tmp_path, monkeypatch, cache_env)[source]¶
[tool.X] with native_format=’json’ produces a valid JSON cached file.
- tests.test_tool_runner.test_resolve_config_cwd_write_no_config_flag(tmp_path, monkeypatch)[source]¶
CWD-discovery tools write translated [tool.X] to the native config path.
- tests.test_tool_runner.test_resolve_config_no_config_flag_no_native_files_raises(tmp_path, monkeypatch)[source]¶
Tools with no config_flag and no native_config_files raise NotImplementedError.
- tests.test_tool_runner.test_resolve_config_bundled_default(tmp_path, monkeypatch, cache_env)[source]¶
Bundled default is cached and passed via –config flag.
- tests.test_tool_runner.test_resolve_config_bare_invocation(tmp_path, monkeypatch)[source]¶
Tool with no config at all gets bare invocation.
- tests.test_tool_runner.test_resolve_config_empty_tool_config_is_not_match(tmp_path, monkeypatch, cache_env)[source]¶
An empty [tool.X] dict does not count as a config match.
- tests.test_tool_runner.BARE_TOOL = ToolSpec(name='sometool', display_name=None, version='1.0.0', package='sometool', executable=None, module=None, native_config_files=(), config_flag=None, native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=False, default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, config_after_subcommand=False, post_process=None, output_flag=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url=None, tag_pattern=None, config_docs_url=None, cli_docs_url=None, docs_notes='')¶
A tool with no bundled default, no native file and no pyproject section.
- tests.test_tool_runner.test_resolve_config_source(tmp_path, monkeypatch, tool, files, expected)[source]¶
The reported config source names where the tool’s settings came from.
- tests.test_tool_runner.test_run_tool_unknown_tool()[source]¶
Raise ValueError for unregistered tool names.
- tests.test_tool_runner.test_run_tool_yamllint_bundled_default(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
yamllint with bundled default builds the correct command.
- tests.test_tool_runner.test_run_tool_ci_flags(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
CI flags are appended when GITHUB_ACTIONS is set.
- tests.test_tool_runner.test_run_tool_native_config_no_extra_flags(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
Tool with native config file gets no config flags from repomatic.
- tests.test_tool_runner.test_run_tool_pyproject_section_cached_config(mock_ci, mock_run, tmp_path, monkeypatch, cache_env)[source]¶
[tool.X] translation writes config to cache and passes via –config.
- tests.test_tool_runner.test_run_tool_forwards_exit_code(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
Tool’s exit code is forwarded unchanged.
- tests.test_tool_runner.test_run_tool_autopep8_default_flags(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
autopep8 runs with all default flags via uvx.
- tests.test_tool_runner.test_run_tool_pyproject_fmt(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
pyproject-fmt runs bare via uvx, with no forced –expand-tables flag.
- tests.test_tool_runner.test_run_tool_mdformat_with_packages(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
mdformat runs via uvx with all plugin packages.
- tests.test_tool_runner.test_run_tool_concurrent_runs_keep_their_cwd_config(mock_ci, mock_install, cache_env, tmp_path, monkeypatch)[source]¶
Two runs sharing a directory each keep a written config for their whole run.
Without the lock, the second run took the first run’s temporary
.mdformat.tomlfor the repository’s own, then lost it halfway when the first run deleted it on the way out.
- tests.test_tool_runner.test_run_tool_runs_unlocked_when_the_cache_is_unwritable(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
A lock file that cannot be created does not stop the run.
- tests.test_tool_runner.test_run_tool_mypy_with_computed_params(mock_ci, mock_metadata_cls, mock_run, tmp_path, monkeypatch)[source]¶
mypy runs via uv run with computed –python-version param.
- tests.test_tool_runner.test_run_tool_mypy_without_computed_params(mock_ci, mock_metadata_cls, mock_run, tmp_path, monkeypatch)[source]¶
mypy runs without computed params when Metadata returns None.
- tests.test_tool_runner.test_run_tool_mypy_no_lockfile_falls_back_isolated(mock_ci, mock_metadata_cls, mock_run, tmp_path, monkeypatch)[source]¶
Without a uv.lock, mypy runs isolated instead of demanding
--frozen.A repository holding a
pyproject.tomlwith only[tool.*]tables (a dotfiles repo linting standalone scripts) has no lockfile, souv run --frozenaborts with “Unable to find lockfile atuv.lock” before mypy ever starts.
- tests.test_tool_runner.test_run_tool_nuitka_uses_module_invocation(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
nuitka runs via
python -m nuitkato avoid Windows script resolution issues.
- tests.test_tool_runner.test_run_tool_nuitka_nofollow_imports_default(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
nuitka excludes tkinter by default via nuitka.nofollow-imports.
- tests.test_tool_runner.test_run_tool_nuitka_nofollow_imports_override(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
An explicit nuitka.nofollow-imports replaces the tkinter default.
- tests.test_tool_runner.test_run_tool_creates_output_parent_directory(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
run_tool creates parent directories for –output file paths.
- tests.test_tool_runner.test_run_tool_creates_output_parent_equals_form(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
The
--output=pathspelling gets its parent directory created too.
- tests.test_tool_runner.test_run_tool_output_existing_directory_is_noop(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
run_tool does not fail when –output parent directory already exists.
- tests.test_tool_runner.test_run_tool_no_output_flag_skips_mkdir(mock_ci, mock_run, tmp_path, monkeypatch)[source]¶
run_tool without –output does not create any directories.
- tests.test_tool_runner.test_get_data_file_path_existing()[source]¶
Bundled data files are accessible via get_data_file_path.
- tests.test_tool_runner.test_get_data_file_path_missing()[source]¶
Missing data files raise FileNotFoundError.
- tests.test_tool_runner.test_find_unmodified_configs_exact_match(tmp_path, monkeypatch)[source]¶
Native config file matching bundled default is flagged as unmodified.
- tests.test_tool_runner.test_find_unmodified_configs_trailing_whitespace(tmp_path, monkeypatch)[source]¶
Trailing whitespace differences are normalized away.
- tests.test_tool_runner.test_find_unmodified_configs_honors_root(tmp_path, monkeypatch)[source]¶
Detection reads under
root, never the current directory.run_initjoins the returned paths against itsoutput_dir, so a scan anchored elsewhere would report on one tree and let--delete-unmodifieddelete from another.
- tests.test_tool_runner.test_find_unmodified_configs_modified_content(tmp_path, monkeypatch)[source]¶
Native config with different content is not flagged.
- tests.test_tool_runner.test_find_unmodified_configs_no_file(tmp_path, monkeypatch)[source]¶
No native config on disk returns empty list.
- tests.test_tool_runner.test_find_unmodified_configs_multiple_tools(tmp_path, monkeypatch)[source]¶
Redundant files for multiple tools are all detected.
- tests.test_tool_runner.test_find_unmodified_configs_alternative_filename(tmp_path, monkeypatch)[source]¶
Alternative native config filename (.yamllint.yml) is also checked.
- tests.test_tool_runner.test_directive_yaml_options_regex(before, after)[source]¶
YAML-block directive options are converted to field-list syntax.
- tests.test_tool_runner.test_directive_yaml_options_regex_no_false_positives(content)[source]¶
Non-directive YAML blocks and horizontal rules are left untouched.
- tests.test_tool_runner.test_fix_myst_directives_in_place(tmp_path)[source]¶
Post-processor rewrites files in-place and skips unchanged files.
- tests.test_tool_runner.test_fix_myst_directives_multiple_directives(tmp_path)[source]¶
Multiple directive blocks in the same file are all fixed.
- tests.test_tool_runner.test_escaped_colon_fence_regex(before, after)[source]¶
Escaped colon-fence directives are un-escaped, nesting included.
- tests.test_tool_runner.test_escaped_colon_fence_regex_no_false_positives(content)[source]¶
Unescaped fences, deflist syntax and stray escapes are left untouched.
- tests.test_tool_runner.test_fix_myst_directives_colon_fence_in_place(tmp_path)[source]¶
Both fixups run together: YAML options and escaped colon fences.
- tests.test_tool_runner.test_fix_myst_directives_skips_a_file_that_is_not_text(tmp_path)[source]¶
A file that does not decode as UTF-8 is not one mdformat wrote.
run_toolhands over every file whose content changed below a directory argument, whoever wrote it: an editor that refreshes.git/indexduring the run is enough. The page listed after the photo proves the loop goes on.
- tests.test_tool_runner.test_check_bypasses_post_process()[source]¶
check_bypasses_post_process is True only for a post_process tool in check mode.
- tests.test_tool_runner.test_run_tool_warns_when_check_bypasses_post_process(mock_ci, mock_install, mock_run, tmp_path, monkeypatch, caplog)[source]¶
mdformat –check warns that the post-process fixup is skipped.
- tests.test_tool_runner.test_run_tool_no_check_warning_in_write_mode(mock_ci, mock_install, mock_run, tmp_path, monkeypatch, caplog)[source]¶
mdformat in write mode (no check flag) emits no check-mode warning.
- tests.test_tool_runner.test_run_tool_post_processes_files_below_a_directory(mock_ci, mock_install, mock_run, tmp_path, monkeypatch)[source]¶
mdformat pointed at a directory gets its fixup on each file it rewrote.
A file the run left alone keeps its content. It stands for a file that the exclude list of the tool skipped, which the fixup must not rewrite either.
- tests.test_tool_runner.test_resolve_config_flags_format()[source]¶
A FLAGS-format spec turns
[tool.X]into CLI args, not a file.
- tests.test_tool_runner.test_resolve_config_flags_format_empty()[source]¶
No
[tool.X]section yields no flags and no cleanup file.
- tests.test_tool_runner.test_native_format_flags_serialize_raises()[source]¶
FLAGS is not a file format, so serialize() rejects it.
- tests.test_tool_runner.test_strip_components_resolves_per_platform()[source]¶
A dict
strip_componentsresolves likearchive_formatdoes.ghis the reason both exist: its Linux and macOS archives nest under a versioned directory while the Windows zip does not, so one count cannot serve every platform.
- tests.test_tool_runner.test_strip_components_plain_int_applies_everywhere()[source]¶
A bare
intkeeps applying to every platform, as before.
- tests.test_tool_runner.test_gh_spec_matches_upstream_archive_layout()[source]¶
gh’s declared layout matches what cli/cli actually publishes.Locks in the asymmetry that motivated per-platform
strip_components: a future release that flattened the Linux tarball, or nested the Windows zip, would extract the wrong path and only fail during a release.
- tests.test_tool_runner.test_ensure_binary_rejects_unknown_tool()[source]¶
An unregistered name fails loudly rather than falling back to $PATH.
- tests.test_tool_runner.test_ensure_binary_rejects_non_binary_tool()[source]¶
A uvx- or npm-backed tool has no binary to place, so it is refused.
ruffinstalls through uvx; asking for its executable path would silently return nothing useful, so the seam rejects it instead.
- tests.test_tool_runner.test_image_optimizers_resolve_through_registry_or_path()[source]¶
oxipngcomes from the registry;jpegoptimstill needs$PATH.Guards the split documented in
_check_tool: oxipng ships prebuilt binaries and is pinned and checksummed, while jpegoptim publishes source only and stays a distro package.
- tests.test_tool_runner.test_resolve_default_args_without_defaults()[source]¶
A tool declaring none runs bare, exactly as before.
- tests.test_tool_runner.test_resolve_default_args_flags_only()[source]¶
A tool whose CI shape is a literal argument needs no inventory.
- tests.test_tool_runner.test_resolve_default_args_appends_the_inventory(tmp_path, monkeypatch)[source]¶
Declared paths resolve to the repository’s own matching files.
- tests.test_tool_runner.test_resolve_default_args_keeps_default_args_ahead_of_paths(tmp_path, monkeypatch)[source]¶
A subcommand stays the first token, where the tool’s parser wants it.
- tests.test_tool_runner.test_resolve_default_args_splits_per_file(tmp_path, monkeypatch)[source]¶
per_filemirrorsxargs -n1: one invocation per target.
- tests.test_tool_runner.test_resolve_default_args_empty_inventory_means_skip(tmp_path, monkeypatch)[source]¶
No matching file resolves to
None, never to a pathless invocation.This is the guard the whole feature exists for: a write-mode formatter handed zero paths walks the entire tree instead of doing nothing.
- tests.test_tool_runner.test_run_tool_skips_a_tool_with_no_targets(mock_ci, mock_run, mock_install, tmp_path, monkeypatch)[source]¶
An empty inventory means the tool is never spawned.
- tests.test_tool_runner.test_run_tool_runs_once_per_file(mock_ci, mock_run, mock_install, tmp_path, monkeypatch)[source]¶
A per-file tool is invoked once per target, installed once.
- tests.test_tool_runner.test_run_tool_reports_the_first_failure_across_batches(mock_ci, mock_run, mock_install, tmp_path, monkeypatch)[source]¶
Every target still runs, and no later success masks an earlier failure.
- tests.test_tool_runner.test_run_tool_explicit_args_suppress_defaults(mock_ci, mock_run, mock_install, tmp_path, monkeypatch)[source]¶
A caller-supplied argument means the caller drives the whole argv.
Without the all-or-nothing rule, biome’s
formatdefault would splice ahead of an explicitcheck, building a command neither one asked for.
- tests.test_tool_runner.test_default_paths_names_a_real_inventory_group(spec)[source]¶
A tool’s declared target group exists and yields paths.
The field is a string because the inventory resolves against the working directory at call time, so a typo would otherwise surface as an
AttributeErrormid-run, in CI, after the tool was already installed.
- tests.test_tool_runner.test_per_file_requires_default_paths(spec)[source]¶
per_filesplits a target list, so it is meaningless without one.
- tests.test_tool_runner.MESSY_PYPROJECT = '[project]\nname="orchard"\nversion="1.0"\n'¶
An unformatted
pyproject.toml, the input a formatter is expected to rewrite.
- tests.test_tool_runner.test_run_tool_reports_a_rewrite_status_that_rewrote_nothing(mock_ci, mock_run, mock_install, tmp_path, monkeypatch, caplog)[source]¶
A formatter claiming a rewrite it did not perform is reported as a crash.
This is how pyproject-fmt dies on a
PanicException: exit code 1, the same one it uses for a successful reformat, and an untouched file. Callers have to tolerate that code, so without the file check the crash reads as a normal run and the autofix job goes green having formatted nothing.
- tests.test_tool_runner.test_run_tool_passes_a_rewrite_status_that_rewrote_a_file(mock_ci, mock_run, mock_install, tmp_path, monkeypatch)[source]¶
The same exit code is passed through untouched when a file did change.
- tests.test_tool_runner.test_run_tool_leaves_other_tools_exit_codes_alone(mock_ci, mock_run, mock_install, tmp_path, monkeypatch)[source]¶
A tool declaring no rewrite status keeps reporting its own failures.
mdformat exits 1 on a genuine error, never to announce a rewrite, so the contradiction check must not reinterpret it.
- tests.test_tool_runner.test_digest_targets_stays_out_of_git_directories(tmp_path, monkeypatch)[source]¶
A walk below a directory argument never enters a
.gitdirectory.No tool rewrites a file there, and another process can. An index that an editor refreshes during the run would read as a file the tool rewrote. A
.gitignorefile is not in that directory, and stays in the walk.
- tests.test_tool_runner.test_verify_via_write_path_propagates_a_crash(mock_run_tool, tmp_path, monkeypatch)[source]¶
A tool that dies on the copies is a failure, not a clean bill of health.
The copies come back unformatted, so comparing them to the originals finds no difference. Reporting that as “already formatted” would turn every crash into a passing verification.
- tests.test_tool_runner.test_verify_via_write_path_accepts_the_working_directory(mock_run_tool, tmp_path, monkeypatch)[source]¶
A path resolving to the working directory verifies the default set.
The scratch root is created inside the working directory, so mirroring
.under it makes the scratch its own copy destination andshutil.copytreedies on it as an existing path. It reached users as a bareFileExistsError: .repomatic-verify-<rand>, which reads as a repository problem rather than a rejected argument.The collision happens while copying, before the tool runs, so a stubbed
run_toolkeeps the test off the network.
- tests.test_tool_runner.test_verify_via_write_path_fails_when_nothing_is_copied(mock_run_tool, tool, extra_args, tmp_path, monkeypatch)[source]¶
A verification that copied no path measured nothing, so it cannot pass.
An exit code of
0would read as a clean tree to any gate running it.
- tests.test_tool_runner.test_verify_via_write_path_reports_a_path_it_cannot_copy(mock_run_tool, tmp_path, monkeypatch, caplog)[source]¶
A copy that fails is one logged error that names the path, not a traceback.
Nothing was measured, so the answer is the
2of a call that copied no path, and the scratch directory does not outlive the call.