This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new bc788418d95 Fix excluded provider tests crashing pytest (#63918)
bc788418d95 is described below
commit bc788418d95eefe7e9f03c86731844160bcfb529
Author: Dev-iL <[email protected]>
AuthorDate: Thu Mar 19 12:48:30 2026 +0200
Fix excluded provider tests crashing pytest (#63918)
When running provider tests (e.g., Providers[google]) on a Python version
where the provider is excluded, generate_args_for_pytest removes the test
directories but the skip check in _run_test only triggers when its own
--ignore filter removes something. Since the directories are already gone,
pytest runs with zero test directories and crashes on unrecognized custom
arguments like --run-db-tests-only.
Add are_all_test_paths_excluded() that explicitly checks whether all test
paths for the given test type are in the excluded/suspended provider set,
using the same provider.yaml data. Call it early in _run_test to skip
before any Docker operations.
---
.../airflow_breeze/commands/testing_commands.py | 10 +++++
dev/breeze/src/airflow_breeze/utils/run_tests.py | 38 ++++++++++++++++++
dev/breeze/tests/test_run_test_args.py | 45 ++++++++++++++++++++++
3 files changed, 93 insertions(+)
diff --git a/dev/breeze/src/airflow_breeze/commands/testing_commands.py
b/dev/breeze/src/airflow_breeze/commands/testing_commands.py
index a6aa6ef021c..3ee2e605614 100644
--- a/dev/breeze/src/airflow_breeze/commands/testing_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/testing_commands.py
@@ -114,6 +114,7 @@ from airflow_breeze.utils.parallel import (
from airflow_breeze.utils.path_utils import AIRFLOW_CTL_ROOT_PATH, FILES_PATH,
cleanup_python_generated_files
from airflow_breeze.utils.run_tests import (
TASK_SDK_INTEGRATION_TESTS_ROOT_PATH,
+ are_all_test_paths_excluded,
file_name_from_test_type,
generate_args_for_pytest,
run_docker_compose_tests,
@@ -206,6 +207,15 @@ def _run_test(
"[error]Only 'Providers' test type can specify actual tests with
\\[\\][/]"
)
sys.exit(1)
+ if are_all_test_paths_excluded(
+ test_group=shell_params.test_group,
+ test_type=shell_params.test_type,
+ python_version=python_version,
+ skip_db_tests=shell_params.skip_db_tests,
+ parallel_test_types_list=shell_params.parallel_test_types_list,
+ integration=shell_params.integration,
+ ):
+ return 0, f"Skipped test, no tests needed: {shell_params.test_type}"
compose_project_name, project_name = _get_project_names(shell_params)
env = shell_params.env_variables_for_docker_commands
down_cmd = [
diff --git a/dev/breeze/src/airflow_breeze/utils/run_tests.py
b/dev/breeze/src/airflow_breeze/utils/run_tests.py
index b14818f028c..5cd4e6692d8 100644
--- a/dev/breeze/src/airflow_breeze/utils/run_tests.py
+++ b/dev/breeze/src/airflow_breeze/utils/run_tests.py
@@ -227,6 +227,44 @@ def get_excluded_test_provider_folders(python_version:
str) -> list[str]:
return get_test_folders(excluded_folders)
+def are_all_test_paths_excluded(
+ *,
+ test_group: GroupOfTests,
+ test_type: str,
+ python_version: str,
+ skip_db_tests: bool = False,
+ parallel_test_types_list: list[str] | None = None,
+ integration: tuple | None = None,
+) -> bool:
+ """Check if all test paths for the given test type are excluded or
suspended.
+
+ Uses the same provider.yaml exclusion data as generate_args_for_pytest to
determine
+ whether every test directory that would be generated for this test type is
in the
+ excluded or suspended provider set.
+
+ Returns False when test_type is "None" (user provides explicit paths via
extra_pytest_args)
+ or when any test path remains after exclusions.
+ """
+ if skip_db_tests and parallel_test_types_list:
+ initial_paths = convert_parallel_types_to_folders(
+ test_group=test_group,
+ parallel_test_types_list=parallel_test_types_list,
+ )
+ else:
+ initial_paths = convert_test_type_to_pytest_args(
+ test_group=test_group,
+ test_type=test_type,
+ integration=integration,
+ )
+ # Filter to only actual test directory paths (not pytest flags like -m,
--include-quarantined)
+ test_dir_paths = [p for p in initial_paths if not p.startswith("-")]
+ if not test_dir_paths:
+ return False
+ excluded = set(get_excluded_test_provider_folders(python_version))
+ suspended = set(get_suspended_test_provider_folders())
+ return all(p in excluded | suspended for p in test_dir_paths)
+
+
TEST_TYPE_CORE_MAP_TO_PYTEST_ARGS: dict[str, list[str]] = {
"Always": ["airflow-core/tests/unit/always"],
"API": ["airflow-core/tests/unit/api",
"airflow-core/tests/unit/api_fastapi"],
diff --git a/dev/breeze/tests/test_run_test_args.py
b/dev/breeze/tests/test_run_test_args.py
index 446d49fe0dd..454787eb74b 100644
--- a/dev/breeze/tests/test_run_test_args.py
+++ b/dev/breeze/tests/test_run_test_args.py
@@ -107,3 +107,48 @@ def
test_test_is_skipped_if_all_are_ignored(mock_run_command):
)
mock_run_command.assert_called_once() # called only to compose down
+
+
+def test_test_is_skipped_when_all_providers_excluded_for_python_version(
+ mock_run_command, mock_get_excluded_provider_folders
+):
+ """When all providers in the test type are excluded for the Python
version, skip without Docker calls."""
+ mock_get_excluded_provider_folders.return_value = ["http"]
+ return_code, message = _run_test(
+ shell_params=ShellParams(test_group=GroupOfTests.PROVIDERS,
test_type="Providers[http]"),
+ extra_pytest_args=(),
+ python_version="3.14",
+ output=None,
+ test_timeout=60,
+ skip_docker_compose_down=True,
+ )
+ assert return_code == 0
+ assert "Skipped" in message
+ mock_run_command.assert_not_called()
+
+
+def test_test_is_not_skipped_when_some_providers_remain(mock_run_command,
mock_get_excluded_provider_folders):
+ """When only some providers are excluded, the test should still run."""
+ mock_get_excluded_provider_folders.return_value = ["http"]
+ _run_test(
+ shell_params=ShellParams(test_group=GroupOfTests.PROVIDERS,
test_type="Providers[http,standard]"),
+ extra_pytest_args=(),
+ python_version="3.14",
+ output=None,
+ test_timeout=60,
+ skip_docker_compose_down=True,
+ )
+ assert mock_run_command.call_count >= 2 # compose down + compose run
+
+
+def test_none_test_type_with_extra_args_does_not_skip(mock_run_command):
+ """test_type=None with user-provided test paths via extra_pytest_args must
not skip."""
+ _run_test(
+ shell_params=ShellParams(test_group=GroupOfTests.CORE,
test_type="None"),
+
extra_pytest_args=("airflow-core/tests/unit/serialization/test_helpers.py",),
+ python_version="3.14",
+ output=None,
+ test_timeout=60,
+ skip_docker_compose_down=True,
+ )
+ assert mock_run_command.call_count >= 2 # compose down + compose run