This is an automated email from the ASF dual-hosted git repository.

jscheffl pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new 4256cc34029 [v3-1-test] Fix flaky tests related to github api rate 
limits (#59879) (#59884)
4256cc34029 is described below

commit 4256cc34029526a2b4b03ba150dbed973f0ac900
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Dec 29 14:07:15 2025 +0100

    [v3-1-test] Fix flaky tests related to github api rate limits (#59879) 
(#59884)
    
    - Don't try to access the json of a non-200 response.
    - Replace API calls with mocks.
    (cherry picked from commit dae3441b95e427ab8e92c1a89bf5c19c71945e20)
    
    Co-authored-by: Dev-iL <[email protected]>
---
 .../src/airflow_breeze/utils/selective_checks.py   | 13 ++-
 dev/breeze/tests/conftest.py                       |  8 ++
 dev/breeze/tests/test_selective_checks.py          | 98 +++++++++++++++++++++-
 3 files changed, 116 insertions(+), 3 deletions(-)

diff --git a/dev/breeze/src/airflow_breeze/utils/selective_checks.py 
b/dev/breeze/src/airflow_breeze/utils/selective_checks.py
index 9e71370e247..3f6bb5a26af 100644
--- a/dev/breeze/src/airflow_breeze/utils/selective_checks.py
+++ b/dev/breeze/src/airflow_breeze/utils/selective_checks.py
@@ -1318,7 +1318,11 @@ class SelectiveChecks:
 
         response = requests.get(url, headers=headers, params=payload)
         if response.status_code != 200:
-            get_console().print(f"[red]Error while listing workflow runs 
error: {response.json()}.\n")
+            try:
+                error_msg = response.json()
+            except ValueError:
+                error_msg = response.text[:200]  # Truncate long HTML responses
+            get_console().print(f"[red]Error while listing workflow runs 
error: {error_msg}.\n")
             return None
         runs = response.json().get("workflow_runs", [])
         if not runs:
@@ -1328,6 +1332,13 @@ class SelectiveChecks:
             return None
         jobs_url = runs[0].get("jobs_url")
         jobs_response = requests.get(jobs_url, headers=headers)
+        if jobs_response.status_code != 200:
+            try:
+                error_msg = jobs_response.json()
+            except ValueError:
+                error_msg = jobs_response.text[:200]
+            get_console().print(f"[red]Error while listing jobs error: 
{error_msg}.\n")
+            return None
         jobs = jobs_response.json().get("jobs", [])
         if not jobs:
             get_console().print("[yellow]No jobs information found for jobs 
%s.\n", jobs_url)
diff --git a/dev/breeze/tests/conftest.py b/dev/breeze/tests/conftest.py
index f968e10c812..68a1d4cf8d1 100644
--- a/dev/breeze/tests/conftest.py
+++ b/dev/breeze/tests/conftest.py
@@ -36,3 +36,11 @@ def pytest_unconfigure(config):
 @pytest.fixture(autouse=True)
 def clear_clearable_cache():
     clear_all_cached_functions()
+
+
[email protected]
+def json_decode_error():
+    """Provide a requests.exceptions.JSONDecodeError for mocking API 
failures."""
+    import requests
+
+    return requests.exceptions.JSONDecodeError("", "", 0)
diff --git a/dev/breeze/tests/test_selective_checks.py 
b/dev/breeze/tests/test_selective_checks.py
index 714a157519d..3c1808691c0 100644
--- a/dev/breeze/tests/test_selective_checks.py
+++ b/dev/breeze/tests/test_selective_checks.py
@@ -1760,12 +1760,26 @@ def test_expected_output_pull_request_v2_7(
         ),
     ],
 )
[email protected]("os.environ", {"GITHUB_TOKEN": "test_token"})
+@patch("requests.get")
 def test_expected_output_push(
+    mock_get,
     files: tuple[str, ...],
     pr_labels: tuple[str, ...],
     default_branch: str,
     expected_outputs: dict[str, str],
 ):
+    # Mock GitHub API calls for runner_type property (used in PUSH events)
+    workflow_response = Mock()
+    workflow_response.status_code = 200
+    workflow_response.json.return_value = {"workflow_runs": [{"jobs_url": 
"https://api.github.com/jobs/123"}]}
+    jobs_response = Mock()
+    jobs_response.status_code = 200
+    jobs_response.json.return_value = {
+        "jobs": [{"name": "Basic tests (ubuntu-22.04)", "labels": 
["ubuntu-22.04"]}]
+    }
+    mock_get.side_effect = [workflow_response, jobs_response]
+
     stderr = SelectiveChecks(
         files=files,
         commit_ref=NEUTRAL_COMMIT,
@@ -1977,7 +1991,20 @@ def test_expected_output_pull_request_target(
         GithubEvents.SCHEDULE,
     ],
 )
-def 
test_no_commit_provided_trigger_full_build_for_any_event_type(github_event):
[email protected]("os.environ", {"GITHUB_TOKEN": "test_token"})
+@patch("requests.get")
+def test_no_commit_provided_trigger_full_build_for_any_event_type(mock_get, 
github_event):
+    # Mock GitHub API calls for runner_type property (used in PUSH/SCHEDULE 
events)
+    workflow_response = Mock()
+    workflow_response.status_code = 200
+    workflow_response.json.return_value = {"workflow_runs": [{"jobs_url": 
"https://api.github.com/jobs/123"}]}
+    jobs_response = Mock()
+    jobs_response.status_code = 200
+    jobs_response.json.return_value = {
+        "jobs": [{"name": "Basic tests (ubuntu-22.04)", "labels": 
["ubuntu-22.04"]}]
+    }
+    mock_get.side_effect = [workflow_response, jobs_response]
+
     stderr = SelectiveChecks(
         files=(),
         commit_ref="",
@@ -2013,7 +2040,20 @@ def 
test_no_commit_provided_trigger_full_build_for_any_event_type(github_event):
         GithubEvents.SCHEDULE,
     ],
 )
-def test_files_provided_trigger_full_build_for_any_event_type(github_event):
[email protected]("os.environ", {"GITHUB_TOKEN": "test_token"})
+@patch("requests.get")
+def test_files_provided_trigger_full_build_for_any_event_type(mock_get, 
github_event):
+    # Mock GitHub API calls for runner_type property (used in PUSH/SCHEDULE 
events)
+    workflow_response = Mock()
+    workflow_response.status_code = 200
+    workflow_response.json.return_value = {"workflow_runs": [{"jobs_url": 
"https://api.github.com/jobs/123"}]}
+    jobs_response = Mock()
+    jobs_response.status_code = 200
+    jobs_response.json.return_value = {
+        "jobs": [{"name": "Basic tests (ubuntu-22.04)", "labels": 
["ubuntu-22.04"]}]
+    }
+    mock_get.side_effect = [workflow_response, jobs_response]
+
     stderr = SelectiveChecks(
         files=(
             "airflow-core/src/airflow/ui/src/pages/Run/Details.tsx",
@@ -2476,6 +2516,7 @@ def test_get_job_label(mock_get):
     workflow_response.json.return_value = {"workflow_runs": [{"jobs_url": 
"https://api.github.com/jobs/123"}]}
 
     jobs_response = Mock()
+    jobs_response.status_code = 200
     jobs_response.json.return_value = {
         "jobs": [
             {"name": "Basic tests (ubuntu-22.04)", "labels": ["ubuntu-22.04"]},
@@ -2505,6 +2546,7 @@ def test_get_job_label_not_found(mock_get):
     workflow_response.json.return_value = {"workflow_runs": [{"jobs_url": 
"https://api.github.com/jobs/123"}]}
 
     jobs_response = Mock()
+    jobs_response.status_code = 200
     jobs_response.json.return_value = {
         "jobs": [
             {"name": "Basic tests (ubuntu-22.04)", "labels": []},
@@ -2519,6 +2561,57 @@ def test_get_job_label_not_found(mock_get):
     assert result is None
 
 
[email protected](
+    ("workflow_status", "jobs_status", "expected_result"),
+    [
+        pytest.param(504, 200, None, id="workflow_api_504_error"),
+        pytest.param(200, 503, None, id="jobs_api_503_error"),
+        pytest.param(200, 200, "ubuntu-22.04", id="both_apis_200_success"),
+    ],
+)
+@patch("requests.get")
[email protected]("os.environ", {"GITHUB_TOKEN": "test_token"})
+def test_get_job_label_api_status_codes(
+    mock_get, workflow_status, jobs_status, expected_result, json_decode_error
+):
+    """Test that get_job_label handles various HTTP status codes correctly."""
+    selective_checks = SelectiveChecks(
+        files=(),
+        github_event=GithubEvents.PULL_REQUEST,
+        github_repository="apache/airflow",
+        github_context_dict={},
+    )
+
+    workflow_response = Mock()
+    workflow_response.status_code = workflow_status
+    if workflow_status == 200:
+        workflow_response.json.return_value = {
+            "workflow_runs": [{"jobs_url": "https://api.github.com/jobs/123"}]
+        }
+    else:
+        workflow_response.json.side_effect = json_decode_error
+        workflow_response.text = "<html>Gateway Timeout</html>"
+
+    jobs_response = Mock()
+    jobs_response.status_code = jobs_status
+    if jobs_status == 200:
+        jobs_response.json.return_value = {
+            "jobs": [
+                {"name": "Basic tests (ubuntu-22.04)", "labels": 
["ubuntu-22.04"]},
+                {"name": "Other job", "labels": ["ubuntu-22.04"]},
+            ]
+        }
+    else:
+        jobs_response.json.side_effect = json_decode_error
+        jobs_response.text = "<html>Service Unavailable</html>"
+
+    mock_get.side_effect = [workflow_response, jobs_response]
+
+    result = selective_checks.get_job_label("push", "main")
+
+    assert result == expected_result
+
+
 def test_runner_type_pr():
     selective_checks = SelectiveChecks(github_event=GithubEvents.PULL_REQUEST)
 
@@ -2542,6 +2635,7 @@ def test_runner_type_schedule(mock_get):
     workflow_response.json.return_value = {"workflow_runs": [{"jobs_url": 
"https://api.github.com/jobs/123"}]}
 
     jobs_response = Mock()
+    jobs_response.status_code = 200
     jobs_response.json.return_value = {
         "jobs": [
             {"name": "Basic tests / Test git clone on Windows", "labels": 
["windows-2025"]},

Reply via email to