From 902e2aed17d9a780831a8b8fb031dc9a4067d9e0 Mon Sep 17 00:00:00 2001 From: Hiroshi Nishio Date: Mon, 23 Feb 2026 21:12:28 -0800 Subject: [PATCH 1/2] Skip branch updates for test-only pushes when strict=False --- CLAUDE.md | 17 ++ .../branches/get_required_status_checks.py | 35 +++- .../test_get_required_status_checks.py | 58 +++--- services/github/types/webhook/push.py | 8 + services/webhook/push_handler.py | 23 +++ .../webhook/successful_check_suite_handler.py | 12 +- services/webhook/test_push_handler.py | 191 ++++++++++++++++++ 7 files changed, 306 insertions(+), 38 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b852dc40a..c248efaef 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -270,6 +270,23 @@ def test_function_accumulates_tokens(): **Remember**: Tests should provide confidence that the code works, not just that it compiles. +## CRITICAL: NEVER Dismiss Test Failures When Fixing PRs + +**NEVER say "this test is not part of this PR" or "our PR's test passed fine" or "this is a pre-existing issue on the base branch".** + +This is the WORST possible response. CI failed. The PR is blocked. Fix it. + +- Do NOT distinguish between "our test" and "other tests" - ALL failing tests are your problem +- Do NOT blame the base branch, other developers, or previous PRs +- Do NOT report which tests "passed fine" as if that's relevant when CI is red +- When reporting root cause and fix, focus on WHAT failed and HOW you fixed it - not on whose fault it is or which PR introduced it + +**BAD (will make user angry):** +- "The failing test ApplicationTest::test_getByApplicationNo is not part of this PR - it's a pre-existing test in the base branch. Our PR's test AnnotationElectricalOutletSpotModelTest passed fine." + +**GOOD:** +- "ApplicationTest::test_getByApplicationNo failed because line 351 was missing `application_no` in the factory call. Fixed by adding the missing parameter." + ## Proactive Code Fixes When refactoring or replacing old systems, always be PROACTIVE and think comprehensively. Don't wait for the user to point out every piece of old code that needs to fix. diff --git a/services/github/branches/get_required_status_checks.py b/services/github/branches/get_required_status_checks.py index fccedc69c..4280175b1 100644 --- a/services/github/branches/get_required_status_checks.py +++ b/services/github/branches/get_required_status_checks.py @@ -1,4 +1,5 @@ # Standard imports +from dataclasses import dataclass from typing import cast # Third party imports @@ -12,7 +13,14 @@ from utils.logging.logging_config import logger -@handle_exceptions(default_return_value=(201, None), raise_on_error=False) +@dataclass +class StatusChecksResult: + status_code: int = 201 + checks: list[str] | None = None + strict: bool = True + + +@handle_exceptions(default_return_value=StatusChecksResult(), raise_on_error=False) def get_required_status_checks(owner: str, repo: str, branch: str, token: str): """https://docs.github.com/en/rest/branches/branch-protection#get-branch-protection""" url = f"{GITHUB_API_URL}/repos/{owner}/{repo}/branches/{branch}/protection" @@ -21,16 +29,26 @@ def get_required_status_checks(owner: str, repo: str, branch: str, token: str): # NOTE: 403 happens when GitHub App lacks "Administration: Read" permission if response.status_code == 403: + strict = True logger.warning( - "No permission to read branch protection for %s/%s/%s", owner, repo, branch + "No permission to read branch protection for %s/%s/%s, assuming strict=%s", + owner, + repo, + branch, + strict, ) - return 403, None + return StatusChecksResult(status_code=403, checks=None, strict=strict) if response.status_code == 404: + strict = False logger.warning( - "No branch protection configured for %s/%s/%s", owner, repo, branch + "No branch protection configured for %s/%s/%s, assuming strict=%s", + owner, + repo, + branch, + strict, ) - return 404, [] + return StatusChecksResult(status_code=404, checks=[], strict=strict) response.raise_for_status() protection = cast(BranchProtection, response.json()) @@ -43,11 +61,14 @@ def get_required_status_checks(owner: str, repo: str, branch: str, token: str): repo, branch, ) - return 200, [] + return StatusChecksResult(status_code=200, checks=[], strict=False) + strict = required_status_checks.get("strict", False) contexts = set(required_status_checks.get("contexts", [])) checks = { check.get("context") for check in required_status_checks.get("checks", []) } - return 200, list(contexts | checks) + return StatusChecksResult( + status_code=200, checks=list(contexts | checks), strict=strict + ) diff --git a/services/github/branches/test_get_required_status_checks.py b/services/github/branches/test_get_required_status_checks.py index 3ede1ec55..e5d6764a1 100644 --- a/services/github/branches/test_get_required_status_checks.py +++ b/services/github/branches/test_get_required_status_checks.py @@ -39,7 +39,7 @@ def test_get_required_status_checks_success( mock_get.return_value = mock_response mock_headers.return_value = {"Authorization": "Bearer test_token"} - status_code, result = get_required_status_checks( + result = get_required_status_checks( owner=test_owner, repo=test_repo, branch="main", token=test_token ) @@ -48,14 +48,15 @@ def test_get_required_status_checks_success( headers={"Authorization": "Bearer test_token"}, timeout=120, ) - assert status_code == 200 - assert result is not None - assert set(result) == { + assert result.status_code == 200 + assert result.checks is not None + assert set(result.checks) == { "ci/circleci: test", "Codecov", "CircleCI Checks", "Aikido Security", } + assert result.strict is True def test_get_required_status_checks_403_no_permission( @@ -71,12 +72,13 @@ def test_get_required_status_checks_403_no_permission( mock_get.return_value = mock_response mock_headers.return_value = {"Authorization": "Bearer test_token"} - status_code, result = get_required_status_checks( + result = get_required_status_checks( owner=test_owner, repo=test_repo, branch="main", token=test_token ) - assert status_code == 403 - assert result is None + assert result.status_code == 403 + assert result.checks is None + assert result.strict is True def test_get_required_status_checks_404_no_protection( @@ -92,12 +94,13 @@ def test_get_required_status_checks_404_no_protection( mock_get.return_value = mock_response mock_headers.return_value = {"Authorization": "Bearer test_token"} - status_code, result = get_required_status_checks( + result = get_required_status_checks( owner=test_owner, repo=test_repo, branch="main", token=test_token ) - assert status_code == 404 - assert not result + assert result.status_code == 404 + assert not result.checks + assert result.strict is False def test_get_required_status_checks_no_required_checks( @@ -114,12 +117,13 @@ def test_get_required_status_checks_no_required_checks( mock_get.return_value = mock_response mock_headers.return_value = {"Authorization": "Bearer test_token"} - status_code, result = get_required_status_checks( + result = get_required_status_checks( owner=test_owner, repo=test_repo, branch="main", token=test_token ) - assert status_code == 200 - assert not result + assert result.status_code == 200 + assert not result.checks + assert result.strict is False def test_get_required_status_checks_only_contexts(test_owner, test_repo, test_token): @@ -140,12 +144,13 @@ def test_get_required_status_checks_only_contexts(test_owner, test_repo, test_to mock_get.return_value = mock_response mock_headers.return_value = {"Authorization": "Bearer test_token"} - status_code, result = get_required_status_checks( + result = get_required_status_checks( owner=test_owner, repo=test_repo, branch="main", token=test_token ) - assert status_code == 200 - assert result == ["ci/circleci: test"] + assert result.status_code == 200 + assert result.checks == ["ci/circleci: test"] + assert result.strict is True def test_get_required_status_checks_only_checks(test_owner, test_repo, test_token): @@ -166,12 +171,13 @@ def test_get_required_status_checks_only_checks(test_owner, test_repo, test_toke mock_get.return_value = mock_response mock_headers.return_value = {"Authorization": "Bearer test_token"} - status_code, result = get_required_status_checks( + result = get_required_status_checks( owner=test_owner, repo=test_repo, branch="main", token=test_token ) - assert status_code == 200 - assert result == ["CircleCI Checks"] + assert result.status_code == 200 + assert result.checks == ["CircleCI Checks"] + assert result.strict is False def test_get_required_status_checks_http_error_500(test_owner, test_repo, test_token): @@ -188,12 +194,13 @@ def test_get_required_status_checks_http_error_500(test_owner, test_repo, test_t mock_get.return_value = mock_response mock_headers.return_value = {"Authorization": "Bearer test_token"} - status_code, result = get_required_status_checks( + result = get_required_status_checks( owner=test_owner, repo=test_repo, branch="main", token=test_token ) - assert status_code == 201 - assert result is None + assert result.status_code == 201 + assert result.checks is None + assert result.strict is True def test_get_required_status_checks_network_error(test_owner, test_repo, test_token): @@ -205,9 +212,10 @@ def test_get_required_status_checks_network_error(test_owner, test_repo, test_to mock_get.side_effect = requests.exceptions.ConnectionError("Network error") mock_headers.return_value = {"Authorization": "Bearer test_token"} - status_code, result = get_required_status_checks( + result = get_required_status_checks( owner=test_owner, repo=test_repo, branch="main", token=test_token ) - assert status_code == 201 - assert result is None + assert result.status_code == 201 + assert result.checks is None + assert result.strict is True diff --git a/services/github/types/webhook/push.py b/services/github/types/webhook/push.py index 4c3ed738e..e65fdd900 100644 --- a/services/github/types/webhook/push.py +++ b/services/github/types/webhook/push.py @@ -1,11 +1,19 @@ from typing import TypedDict + from services.github.types.installation import Installation from services.github.types.repository import Repository from services.github.types.sender import Sender +class PushCommit(TypedDict): + added: list[str] + modified: list[str] + removed: list[str] + + class PushWebhookPayload(TypedDict): ref: str + commits: list[PushCommit] repository: Repository sender: Sender installation: Installation diff --git a/services/webhook/push_handler.py b/services/webhook/push_handler.py index 6f8732244..1e1e1bd43 100644 --- a/services/webhook/push_handler.py +++ b/services/webhook/push_handler.py @@ -1,9 +1,13 @@ +from services.github.branches.get_required_status_checks import ( + get_required_status_checks, +) from services.github.pulls.get_open_pull_requests import get_open_pull_requests from services.github.pulls.update_pull_request_branch import update_pull_request_branch from services.github.token.get_installation_token import get_installation_access_token from services.github.types.webhook.push import PushWebhookPayload from services.supabase.repositories.get_repository import get_repository from utils.error.handle_exceptions import handle_exceptions +from utils.files.is_test_file import is_test_file from utils.logging.logging_config import logger, set_trigger @@ -39,6 +43,25 @@ def handle_push(payload: PushWebhookPayload): token = get_installation_access_token(installation_id=installation_id) + # Check if this is a test-only push and the repo doesn't require up-to-date branches + commits = payload.get("commits", []) + if commits: + all_files: set[str] = set() + for commit in commits: + all_files.update(commit.get("added", [])) + all_files.update(commit.get("modified", [])) + all_files.update(commit.get("removed", [])) + if all_files and all(is_test_file(f) for f in all_files): + protection = get_required_status_checks( + owner=owner_name, repo=repo_name, branch=target_branch, token=token + ) + if not protection.strict: + logger.info( + "Skipping PR branch updates: test-only push to %s and strict=False", + target_branch, + ) + return None + open_prs = get_open_pull_requests(owner=owner_name, repo=repo_name, token=token) if not open_prs: diff --git a/services/webhook/successful_check_suite_handler.py b/services/webhook/successful_check_suite_handler.py index 1024dd2ae..56466fdec 100644 --- a/services/webhook/successful_check_suite_handler.py +++ b/services/webhook/successful_check_suite_handler.py @@ -119,27 +119,27 @@ def handle_successful_check_suite(payload: CheckSuiteCompletedPayload): logger.error(msg) raise RuntimeError(msg) - status_code, required_checks = get_required_status_checks( + protection = get_required_status_checks( owner=owner_name, repo=repo_name, branch=base_branch, token=token ) - if required_checks: - logger.info("Using required status checks: %s", required_checks) + if protection.checks: + logger.info("Using required status checks: %s", protection.checks) for suite in all_suites: app_name = suite["app"]["name"] status = suite["status"] - if app_name in required_checks and status != "completed": + if app_name in protection.checks and status != "completed": logger.info( "Required check '%s' not completed: status=%s", app_name, status ) return logger.info("All required checks completed") else: - if required_checks is None: + if protection.checks is None: logger.info( "Could not read branch protection (status=%s), " "waiting for all check suites to complete", - status_code, + protection.status_code, ) else: logger.info( diff --git a/services/webhook/test_push_handler.py b/services/webhook/test_push_handler.py index 593492d6f..bbee044ef 100644 --- a/services/webhook/test_push_handler.py +++ b/services/webhook/test_push_handler.py @@ -1,5 +1,8 @@ +# pyright: reportUnusedVariable=false from typing import cast from unittest.mock import patch + +from services.github.branches.get_required_status_checks import StatusChecksResult from services.github.types.webhook.push import PushWebhookPayload from services.webhook.push_handler import handle_push @@ -24,6 +27,7 @@ def test_handle_push_tag_push_returns_early( }, "installation": {"id": 789}, "ref": "refs/tags/v1.0.0", + "commits": [], } result = handle_push(cast(PushWebhookPayload, payload)) @@ -56,6 +60,7 @@ def test_handle_push_repository_not_found_returns_early( }, "installation": {"id": 789}, "ref": "refs/heads/main", + "commits": [], } mock_get_repository.return_value = None @@ -90,6 +95,7 @@ def test_handle_push_local_feature_to_remote_feature_not_handled( }, "installation": {"id": 789}, "ref": "refs/heads/feature-branch", + "commits": [], } mock_get_repository.return_value = {"target_branch": "main"} @@ -124,6 +130,7 @@ def test_handle_push_remote_feature_to_remote_staging_not_handled( }, "installation": {"id": 789}, "ref": "refs/heads/staging", + "commits": [], } mock_get_repository.return_value = {"target_branch": "main"} @@ -158,6 +165,7 @@ def test_handle_push_no_open_prs_returns_early( }, "installation": {"id": 789}, "ref": "refs/heads/main", + "commits": [], } mock_get_repository.return_value = {"target_branch": "main"} @@ -196,6 +204,7 @@ def test_handle_push_local_main_to_remote_main_handled( }, "installation": {"id": 789}, "ref": "refs/heads/main", + "commits": [{"added": ["src/app.py"], "modified": [], "removed": []}], } mock_get_repository.return_value = {"target_branch": "main"} @@ -244,6 +253,7 @@ def test_handle_push_remote_feature_to_remote_main_handled( }, "installation": {"id": 789}, "ref": "refs/heads/main", + "commits": [{"added": ["src/app.py"], "modified": [], "removed": []}], } mock_get_repository.return_value = {"target_branch": "main"} @@ -285,6 +295,7 @@ def test_handle_push_with_failed_updates( }, "installation": {"id": 789}, "ref": "refs/heads/main", + "commits": [{"added": ["src/app.py"], "modified": [], "removed": []}], } mock_get_repository.return_value = {"target_branch": "main"} @@ -330,6 +341,7 @@ def test_handle_push_with_merge_conflicts( }, "installation": {"id": 789}, "ref": "refs/heads/main", + "commits": [{"added": ["src/app.py"], "modified": [], "removed": []}], } mock_get_repository.return_value = {"target_branch": "main"} @@ -378,6 +390,7 @@ def test_handle_push_no_gitauto_prs_returns_early( }, "installation": {"id": 789}, "ref": "refs/heads/main", + "commits": [], } mock_get_repository.return_value = {"target_branch": "main"} @@ -394,3 +407,181 @@ def test_handle_push_no_gitauto_prs_returns_early( ) mock_update_pr.assert_not_called() mock_logger.info.assert_not_called() + + +# --- Test-only push filtering tests --- + + +@patch("services.webhook.push_handler.logger") +@patch("services.webhook.push_handler.get_required_status_checks") +@patch("services.webhook.push_handler.update_pull_request_branch") +@patch("services.webhook.push_handler.get_open_pull_requests") +@patch("services.webhook.push_handler.get_installation_access_token") +@patch("services.webhook.push_handler.get_repository") +def test_handle_push_test_only_strict_false_skips_updates( + mock_get_repository, + mock_get_token, + mock_get_open_prs, + mock_update_pr, + mock_get_status_checks, + mock_logger, +): + payload = { + "repository": { + "owner": {"id": 123, "login": "test-owner"}, + "id": 456, + "name": "test-repo", + }, + "installation": {"id": 789}, + "ref": "refs/heads/main", + "commits": [ + {"added": ["tests/test_utils.py"], "modified": [], "removed": []}, + {"added": [], "modified": ["src/test_app.py"], "removed": []}, + ], + } + + mock_get_repository.return_value = {"target_branch": "main"} + mock_get_token.return_value = "test-token" + mock_get_status_checks.return_value = StatusChecksResult( + status_code=200, checks=["ci/test"], strict=False + ) + + result = handle_push(cast(PushWebhookPayload, payload)) + + assert result is None + mock_get_status_checks.assert_called_once_with( + owner="test-owner", repo="test-repo", branch="main", token="test-token" + ) + mock_get_open_prs.assert_not_called() + mock_update_pr.assert_not_called() + mock_logger.info.assert_called_once() + assert "test-only push" in mock_logger.info.call_args[0][0] + + +@patch("services.webhook.push_handler.logger") +@patch("services.webhook.push_handler.get_required_status_checks") +@patch("services.webhook.push_handler.update_pull_request_branch") +@patch("services.webhook.push_handler.get_open_pull_requests") +@patch("services.webhook.push_handler.get_installation_access_token") +@patch("services.webhook.push_handler.get_repository") +def test_handle_push_test_only_strict_true_proceeds( + mock_get_repository, + mock_get_token, + mock_get_open_prs, + mock_update_pr, + mock_get_status_checks, + _mock_logger, +): + payload = { + "repository": { + "owner": {"id": 123, "login": "test-owner"}, + "id": 456, + "name": "test-repo", + }, + "installation": {"id": 789}, + "ref": "refs/heads/main", + "commits": [ + {"added": ["tests/test_utils.py"], "modified": [], "removed": []}, + ], + } + + mock_get_repository.return_value = {"target_branch": "main"} + mock_get_token.return_value = "test-token" + mock_get_status_checks.return_value = StatusChecksResult( + status_code=200, checks=["ci/test"], strict=True + ) + mock_get_open_prs.return_value = [{"number": 1, "title": "PR 1"}] + mock_update_pr.return_value = ("updated", None) + + result = handle_push(cast(PushWebhookPayload, payload)) + + assert result is None + mock_get_status_checks.assert_called_once_with( + owner="test-owner", repo="test-repo", branch="main", token="test-token" + ) + mock_get_open_prs.assert_called_once() + mock_update_pr.assert_called_once_with( + owner="test-owner", repo="test-repo", pr_number=1, token="test-token" + ) + + +@patch("services.webhook.push_handler.logger") +@patch("services.webhook.push_handler.get_required_status_checks") +@patch("services.webhook.push_handler.update_pull_request_branch") +@patch("services.webhook.push_handler.get_open_pull_requests") +@patch("services.webhook.push_handler.get_installation_access_token") +@patch("services.webhook.push_handler.get_repository") +def test_handle_push_mixed_files_proceeds_regardless( + mock_get_repository, + mock_get_token, + mock_get_open_prs, + mock_update_pr, + mock_get_status_checks, + _mock_logger, +): + payload = { + "repository": { + "owner": {"id": 123, "login": "test-owner"}, + "id": 456, + "name": "test-repo", + }, + "installation": {"id": 789}, + "ref": "refs/heads/main", + "commits": [ + { + "added": ["tests/test_utils.py"], + "modified": ["src/app.py"], + "removed": [], + }, + ], + } + + mock_get_repository.return_value = {"target_branch": "main"} + mock_get_token.return_value = "test-token" + mock_get_open_prs.return_value = [{"number": 1, "title": "PR 1"}] + mock_update_pr.return_value = ("updated", None) + + result = handle_push(cast(PushWebhookPayload, payload)) + + assert result is None + mock_get_status_checks.assert_not_called() + mock_get_open_prs.assert_called_once() + mock_update_pr.assert_called_once() + + +@patch("services.webhook.push_handler.logger") +@patch("services.webhook.push_handler.get_required_status_checks") +@patch("services.webhook.push_handler.update_pull_request_branch") +@patch("services.webhook.push_handler.get_open_pull_requests") +@patch("services.webhook.push_handler.get_installation_access_token") +@patch("services.webhook.push_handler.get_repository") +def test_handle_push_empty_commits_proceeds( + mock_get_repository, + mock_get_token, + mock_get_open_prs, + mock_update_pr, + mock_get_status_checks, + _mock_logger, +): + payload = { + "repository": { + "owner": {"id": 123, "login": "test-owner"}, + "id": 456, + "name": "test-repo", + }, + "installation": {"id": 789}, + "ref": "refs/heads/main", + "commits": [], + } + + mock_get_repository.return_value = {"target_branch": "main"} + mock_get_token.return_value = "test-token" + mock_get_open_prs.return_value = [{"number": 1, "title": "PR 1"}] + mock_update_pr.return_value = ("updated", None) + + result = handle_push(cast(PushWebhookPayload, payload)) + + assert result is None + mock_get_status_checks.assert_not_called() + mock_get_open_prs.assert_called_once() + mock_update_pr.assert_called_once() From 0e1fb1f1d3bd7cdd3053f846fc434c421c4fcdd8 Mon Sep 17 00:00:00 2001 From: Hiroshi Nishio Date: Mon, 23 Feb 2026 21:17:26 -0800 Subject: [PATCH 2/2] Update check suite handler tests to use StatusChecksResult dataclass --- .../test_successful_check_suite_handler.py | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/services/webhook/test_successful_check_suite_handler.py b/services/webhook/test_successful_check_suite_handler.py index 2cf3a7075..e0e7ff271 100644 --- a/services/webhook/test_successful_check_suite_handler.py +++ b/services/webhook/test_successful_check_suite_handler.py @@ -3,6 +3,7 @@ from unittest.mock import MagicMock, patch from config import PRODUCT_ID, UTF8 +from services.github.branches.get_required_status_checks import StatusChecksResult from services.github.types.github_types import CheckSuiteCompletedPayload from services.webhook.successful_check_suite_handler import ( handle_successful_check_suite, @@ -39,7 +40,7 @@ def test_handle_successful_check_suite_with_pr( mock_get_check_suites.return_value = [ {"app": {"name": "CircleCI Checks"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["CircleCI Checks"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["CircleCI Checks"], strict=True) with patch( "services.webhook.successful_check_suite_handler.supabase" @@ -140,7 +141,7 @@ def test_handle_successful_check_suite_no_usage_record_found( mock_get_check_suites.return_value = [ {"app": {"name": "GitHub Actions"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["GitHub Actions"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["GitHub Actions"], strict=True) mock_get_pr.return_value = {"mergeable_state": "clean"} mock_check_skip_ci.return_value = False mock_get_files.return_value = [] @@ -238,7 +239,7 @@ def test_auto_merge_success( mock_get_check_suites.return_value = [ {"app": {"name": "CircleCI Checks"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["CircleCI Checks"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["CircleCI Checks"], strict=True) mock_get_pr.return_value = {"mergeable_state": "clean"} mock_check_skip_ci.return_value = False mock_get_files.return_value = [ @@ -367,7 +368,7 @@ def test_auto_merge_multiple_test_files_changed( mock_get_check_suites.return_value = [ {"app": {"name": "CircleCI Checks"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["CircleCI Checks"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["CircleCI Checks"], strict=True) mock_get_pr.return_value = {"mergeable_state": "clean"} mock_check_skip_ci.return_value = False mock_get_files.return_value = [ @@ -519,7 +520,7 @@ def test_auto_merge_with_non_test_files_allowed( mock_get_check_suites.return_value = [ {"app": {"name": "CircleCI Checks"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["CircleCI Checks"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["CircleCI Checks"], strict=True) mock_get_pr.return_value = {"mergeable_state": "clean"} mock_check_skip_ci.return_value = False mock_get_files.return_value = [ @@ -656,7 +657,7 @@ def test_auto_merge_blocked_skips_notification_when_checks_in_progress( }, {"app": {"name": "Cypress"}, "status": "in_progress", "conclusion": None}, ] - mock_get_required_checks.return_value = (200, ["CircleCI Checks"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["CircleCI Checks"], strict=True) mock_get_pr.return_value = {"mergeable_state": "blocked"} with patch( @@ -725,7 +726,7 @@ def test_auto_merge_with_blocked_state( mock_get_check_suites.return_value = [ {"app": {"name": "CircleCI Checks"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["CircleCI Checks"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["CircleCI Checks"], strict=True) mock_get_pr.return_value = {"mergeable_state": "blocked"} with patch( @@ -795,7 +796,7 @@ def test_auto_merge_with_unstable_state( mock_get_check_suites.return_value = [ {"app": {"name": "CircleCI Checks"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["CircleCI Checks"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["CircleCI Checks"], strict=True) mock_get_pr.return_value = {"mergeable_state": "unstable"} mock_check_skip_ci.return_value = False mock_get_files.return_value = [ @@ -874,7 +875,7 @@ def test_auto_merge_with_unknown_state_no_comment( mock_get_check_suites.return_value = [ {"app": {"name": "CircleCI Checks"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["CircleCI Checks"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["CircleCI Checks"], strict=True) mock_get_pr.return_value = {"mergeable_state": "unknown"} with patch( @@ -934,7 +935,7 @@ def test_skip_ci_returns_early( mock_get_check_suites.return_value = [ {"app": {"name": "GitHub Actions"}, "status": "completed"} ] - mock_get_required_checks.return_value = (200, ["GitHub Actions"]) + mock_get_required_checks.return_value = StatusChecksResult(status_code=200, checks=["GitHub Actions"], strict=True) mock_check_skip_ci.return_value = True with patch(