From e55b8f0ecb4ec22353114aa85fbe98b7e67ee1ae Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 20 Jul 2026 17:34:51 +0000 Subject: [PATCH 1/4] test(tools): auto-patch release CLI helpers in conftest.py Add an autouse fixture in conftest.py for tests/tools/private/release that automatically patches run_cmd, Git._run_git, and GitHub._run_gh using pytest-mock. This prevents tests from executing command line tools that could have side-effects. TAG=agy CONV=b4972743-3d1e-4e9a-9623-cf1f25cfdd83 --- tests/tools/private/release/BUILD.bazel | 5 ++++- tests/tools/private/release/conftest.py | 22 ++++++++++++++++++++++ tests/tools/private/release/gh_test.py | 20 ++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/tools/private/release/conftest.py diff --git a/tests/tools/private/release/BUILD.bazel b/tests/tools/private/release/BUILD.bazel index 5ac912a955..317a4626c9 100644 --- a/tests/tools/private/release/BUILD.bazel +++ b/tests/tools/private/release/BUILD.bazel @@ -4,7 +4,10 @@ load("//tests/support/pytest_test:pytest_test.bzl", "pytest_test") py_library( name = "release_test_helper", - srcs = ["release_test_helper.py"], + srcs = [ + "conftest.py", + "release_test_helper.py", + ], target_compatible_with = SUPPORTS_BZLMOD, deps = [ "//tools/private/release:mock_gh", diff --git a/tests/tools/private/release/conftest.py b/tests/tools/private/release/conftest.py new file mode 100644 index 0000000000..f721f22d3e --- /dev/null +++ b/tests/tools/private/release/conftest.py @@ -0,0 +1,22 @@ +import pytest + +pytest_plugins = ["tests.tools.private.release.release_test_helper"] + + +@pytest.fixture(name="auto_patch_cmd_helpers", autouse=True) +def fixture_auto_patch_cmd_helpers(mocker): + """Automatically patches run_cmd, Git, and GitHub CLI helpers. + + This prevents tests from executing command line tools that could have + side-effects. + """ + mock_run_cmd = mocker.patch("tools.private.release.shell.run_cmd") + mocker.patch("tools.private.release.git.run_cmd", mock_run_cmd) + mocker.patch("tools.private.release.gh.run_cmd", mock_run_cmd) + mock_run_git = mocker.patch("tools.private.release.git.Git._run_git") + mock_run_gh = mocker.patch("tools.private.release.gh.GitHub._run_gh") + return { + "run_cmd": mock_run_cmd, + "run_git": mock_run_git, + "run_gh": mock_run_gh, + } diff --git a/tests/tools/private/release/gh_test.py b/tests/tools/private/release/gh_test.py index ccc61e7f21..79c126858f 100644 --- a/tests/tools/private/release/gh_test.py +++ b/tests/tools/private/release/gh_test.py @@ -59,3 +59,23 @@ def test_resolve_pr_number_invalid(mocker, gh): with pytest.raises(ValueError, match="Could not resolve PR reference"): gh.resolve_pr_number("invalid-ref") mock_run_cmd.assert_not_called() + + +def test_auto_patched_helpers_prevent_real_execution(auto_patch_cmd_helpers): + from tools.private.release.gh import GitHub + from tools.private.release.git import Git + from tools.private.release.shell import run_cmd + + # Calling run_cmd directly hits the mock + run_cmd("echo", "test") + auto_patch_cmd_helpers["run_cmd"].assert_called_with("echo", "test") + + # Git._run_git hits the mock + git = Git(".") + git._run_git("status") + auto_patch_cmd_helpers["run_git"].assert_called_with("status") + + # GitHub._run_gh hits the mock + gh_obj = GitHub("foo/bar") + gh_obj._run_gh("issue", "list") + auto_patch_cmd_helpers["run_gh"].assert_called_with("issue", "list") From 6ca639898f2446e3fbe9aaf5e3e5a3a23dfd581a Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 22 Jul 2026 17:16:30 +0000 Subject: [PATCH 2/4] refactor(tools): split conftest.py mocks into individual fixtures Separate mock_run_cmd, mock_run_git, and mock_run_gh into individual fixtures, and collect them in the autouse auto_patch_cmd_helpers fixture. TAG=agy CONV=b4972743-3d1e-4e9a-9623-cf1f25cfdd83 --- tests/tools/private/release/conftest.py | 28 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/tools/private/release/conftest.py b/tests/tools/private/release/conftest.py index f721f22d3e..5a30e063e4 100644 --- a/tests/tools/private/release/conftest.py +++ b/tests/tools/private/release/conftest.py @@ -3,18 +3,34 @@ pytest_plugins = ["tests.tools.private.release.release_test_helper"] +@pytest.fixture(name="mock_run_cmd") +def fixture_mock_run_cmd(mocker): + """Fixture to patch shell.run_cmd and its imports in git and gh modules.""" + mock = mocker.patch("tools.private.release.shell.run_cmd") + mocker.patch("tools.private.release.git.run_cmd", mock) + mocker.patch("tools.private.release.gh.run_cmd", mock) + return mock + + +@pytest.fixture(name="mock_run_git") +def fixture_mock_run_git(mocker): + """Fixture to patch Git._run_git.""" + return mocker.patch("tools.private.release.git.Git._run_git") + + +@pytest.fixture(name="mock_run_gh") +def fixture_mock_run_gh(mocker): + """Fixture to patch GitHub._run_gh.""" + return mocker.patch("tools.private.release.gh.GitHub._run_gh") + + @pytest.fixture(name="auto_patch_cmd_helpers", autouse=True) -def fixture_auto_patch_cmd_helpers(mocker): +def fixture_auto_patch_cmd_helpers(mock_run_cmd, mock_run_git, mock_run_gh): """Automatically patches run_cmd, Git, and GitHub CLI helpers. This prevents tests from executing command line tools that could have side-effects. """ - mock_run_cmd = mocker.patch("tools.private.release.shell.run_cmd") - mocker.patch("tools.private.release.git.run_cmd", mock_run_cmd) - mocker.patch("tools.private.release.gh.run_cmd", mock_run_cmd) - mock_run_git = mocker.patch("tools.private.release.git.Git._run_git") - mock_run_gh = mocker.patch("tools.private.release.gh.GitHub._run_gh") return { "run_cmd": mock_run_cmd, "run_git": mock_run_git, From 1b84cfaff2348cae3b6fbe85dae3b8815688dae7 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 22 Jul 2026 17:17:27 +0000 Subject: [PATCH 3/4] refactor(tools): return AutoPatchCmdHelpers dataclass from conftest.py TAG=agy CONV=b4972743-3d1e-4e9a-9623-cf1f25cfdd83 --- tests/tools/private/release/conftest.py | 22 +++++++++++++++++----- tests/tools/private/release/gh_test.py | 6 +++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/tools/private/release/conftest.py b/tests/tools/private/release/conftest.py index 5a30e063e4..f8798c11c7 100644 --- a/tests/tools/private/release/conftest.py +++ b/tests/tools/private/release/conftest.py @@ -1,8 +1,20 @@ +import dataclasses +from unittest.mock import MagicMock + import pytest pytest_plugins = ["tests.tools.private.release.release_test_helper"] +@dataclasses.dataclass +class AutoPatchCmdHelpers: + """Dataclass holding mocked command helpers.""" + + run_cmd: MagicMock + run_git: MagicMock + run_gh: MagicMock + + @pytest.fixture(name="mock_run_cmd") def fixture_mock_run_cmd(mocker): """Fixture to patch shell.run_cmd and its imports in git and gh modules.""" @@ -31,8 +43,8 @@ def fixture_auto_patch_cmd_helpers(mock_run_cmd, mock_run_git, mock_run_gh): This prevents tests from executing command line tools that could have side-effects. """ - return { - "run_cmd": mock_run_cmd, - "run_git": mock_run_git, - "run_gh": mock_run_gh, - } + return AutoPatchCmdHelpers( + run_cmd=mock_run_cmd, + run_git=mock_run_git, + run_gh=mock_run_gh, + ) diff --git a/tests/tools/private/release/gh_test.py b/tests/tools/private/release/gh_test.py index 79c126858f..b8bdbd975e 100644 --- a/tests/tools/private/release/gh_test.py +++ b/tests/tools/private/release/gh_test.py @@ -68,14 +68,14 @@ def test_auto_patched_helpers_prevent_real_execution(auto_patch_cmd_helpers): # Calling run_cmd directly hits the mock run_cmd("echo", "test") - auto_patch_cmd_helpers["run_cmd"].assert_called_with("echo", "test") + auto_patch_cmd_helpers.run_cmd.assert_called_with("echo", "test") # Git._run_git hits the mock git = Git(".") git._run_git("status") - auto_patch_cmd_helpers["run_git"].assert_called_with("status") + auto_patch_cmd_helpers.run_git.assert_called_with("status") # GitHub._run_gh hits the mock gh_obj = GitHub("foo/bar") gh_obj._run_gh("issue", "list") - auto_patch_cmd_helpers["run_gh"].assert_called_with("issue", "list") + auto_patch_cmd_helpers.run_gh.assert_called_with("issue", "list") From a04306ed0aef3ace54fed8e56a753cd4830320e4 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Wed, 22 Jul 2026 17:20:00 +0000 Subject: [PATCH 4/4] test(tools): move module imports to top in gh_test.py TAG=agy CONV=b4972743-3d1e-4e9a-9623-cf1f25cfdd83 --- tests/tools/private/release/gh_test.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/tools/private/release/gh_test.py b/tests/tools/private/release/gh_test.py index b8bdbd975e..b8c346004b 100644 --- a/tests/tools/private/release/gh_test.py +++ b/tests/tools/private/release/gh_test.py @@ -1,6 +1,8 @@ import pytest +from tools.private.release import shell from tools.private.release.gh import GitHub +from tools.private.release.git import Git pytest_plugins = ["tests.tools.private.release.release_test_helper"] @@ -62,12 +64,8 @@ def test_resolve_pr_number_invalid(mocker, gh): def test_auto_patched_helpers_prevent_real_execution(auto_patch_cmd_helpers): - from tools.private.release.gh import GitHub - from tools.private.release.git import Git - from tools.private.release.shell import run_cmd - # Calling run_cmd directly hits the mock - run_cmd("echo", "test") + shell.run_cmd("echo", "test") auto_patch_cmd_helpers.run_cmd.assert_called_with("echo", "test") # Git._run_git hits the mock