|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import argparse |
3 | 4 | import json |
| 5 | +import runpy |
| 6 | +import sys |
| 7 | +from collections.abc import Sequence |
4 | 8 | from pathlib import Path |
5 | 9 |
|
| 10 | +import pytest |
| 11 | +from pre_commit.clientlib import load_config |
| 12 | +from pre_commit.lang_base import hook_cmd |
| 13 | +from pre_commit.parse_shebang import normalize_cmd |
| 14 | + |
6 | 15 | ROOT = Path(__file__).parents[3] |
7 | 16 |
|
8 | 17 |
|
@@ -83,12 +92,56 @@ def test_global_timeout_is_not_a_pytest_deadline() -> None: |
83 | 92 | assert 'timeout_method = "thread"' in pyproject |
84 | 93 |
|
85 | 94 |
|
86 | | -def test_pre_push_hook_stays_in_the_static_feedback_lane() -> None: |
87 | | - config = (ROOT / ".pre-commit-config.yaml").read_text(encoding="utf-8") |
88 | | - hook = config.split("id: jacobian-pre-push", 1)[1] |
89 | | - |
90 | | - assert "entry: make lint typecheck" in hook |
91 | | - assert "entry: make check" not in hook |
| 95 | +def test_local_hook_commands_have_parseable_entrypoints_and_arguments( |
| 96 | + monkeypatch: pytest.MonkeyPatch, |
| 97 | +) -> None: |
| 98 | + config = load_config(str(ROOT / ".pre-commit-config.yaml")) |
| 99 | + hooks = [ |
| 100 | + hook |
| 101 | + for repo in config["repos"] |
| 102 | + if repo["repo"] == "local" |
| 103 | + for hook in repo["hooks"] |
| 104 | + ] |
| 105 | + assert hooks |
| 106 | + |
| 107 | + class ArgumentsParsedError(Exception): |
| 108 | + pass |
| 109 | + |
| 110 | + original_parse_args = argparse.ArgumentParser.parse_args |
| 111 | + |
| 112 | + def stop_after_parse( |
| 113 | + parser: argparse.ArgumentParser, |
| 114 | + args: Sequence[str] | None = None, |
| 115 | + namespace: argparse.Namespace | None = None, |
| 116 | + ) -> None: |
| 117 | + original_parse_args(parser, args, namespace) |
| 118 | + raise ArgumentsParsedError |
| 119 | + |
| 120 | + monkeypatch.setattr(argparse.ArgumentParser, "parse_args", stop_after_parse) |
| 121 | + |
| 122 | + for hook in hooks: |
| 123 | + command = hook_cmd(hook["entry"], hook["args"]) |
| 124 | + normalize_cmd(command) |
| 125 | + if hook["id"] == "jacobian-pre-push": |
| 126 | + assert command == ("make", "lint", "typecheck") |
| 127 | + continue |
| 128 | + |
| 129 | + assert command[:4] == ("uv", "run", "--locked", "python"), hook["id"] |
| 130 | + script_index = 4 |
| 131 | + assert script_index < len(command), hook["id"] |
| 132 | + script = (ROOT / command[script_index]).resolve() |
| 133 | + assert script.is_relative_to(ROOT) and script.is_file(), hook["id"] |
| 134 | + |
| 135 | + namespace = runpy.run_path(str(script)) |
| 136 | + main = namespace.get("main") |
| 137 | + assert callable(main), hook["id"] |
| 138 | + monkeypatch.setattr( |
| 139 | + sys, |
| 140 | + "argv", |
| 141 | + [str(script), *command[script_index + 1 :]], |
| 142 | + ) |
| 143 | + with pytest.raises(ArgumentsParsedError): |
| 144 | + main() |
92 | 145 |
|
93 | 146 |
|
94 | 147 | def test_process_lane_is_invoked_by_ci() -> None: |
|
0 commit comments