diff --git a/airbyte_cdk/cli/airbyte_cdk/_connector.py b/airbyte_cdk/cli/airbyte_cdk/_connector.py index 2042e0173..17a8bb382 100644 --- a/airbyte_cdk/cli/airbyte_cdk/_connector.py +++ b/airbyte_cdk/cli/airbyte_cdk/_connector.py @@ -123,11 +123,18 @@ def connector_cli_group() -> None: multiple=True, help="Additional argument(s) to pass to pytest. Can be specified multiple times.", ) +@click.option( + "--no-creds", + is_flag=True, + default=False, + help="Skip tests that require credentials (marked with 'requires_creds').", +) def connector_test( connector: str | Path | None = None, *, collect_only: bool = False, pytest_args: list[str] | None = None, + no_creds: bool = False, ) -> None: """Run connector tests. @@ -147,6 +154,9 @@ def connector_test( if collect_only: pytest_args.append("--collect-only") + if no_creds: + pytest_args.extend(["-m", "not requires_creds"]) + run_connector_tests( connector_name=connector_name, connector_directory=connector_directory, diff --git a/airbyte_cdk/cli/airbyte_cdk/_image.py b/airbyte_cdk/cli/airbyte_cdk/_image.py index 4261402a5..c89c05363 100644 --- a/airbyte_cdk/cli/airbyte_cdk/_image.py +++ b/airbyte_cdk/cli/airbyte_cdk/_image.py @@ -100,10 +100,17 @@ def build( "--image", help="Image to test, instead of building a new one.", ) +@click.option( + "--no-creds", + is_flag=True, + default=False, + help="Skip tests that require credentials (marked with 'requires_creds').", +) def image_test( # "image test" command connector: str | None = None, *, image: str | None = None, + no_creds: bool = False, ) -> None: """Test a connector Docker image. @@ -124,7 +131,11 @@ def image_test( # "image test" command connector_name, connector_directory = resolve_connector_name_and_directory(connector) # Select only tests with the 'image_tests' mark - pytest_args = ["-m", "image_tests"] + pytest_filter = "image_tests" + if no_creds: + pytest_filter += " and not requires_creds" + + pytest_args = ["-m", pytest_filter] if not image: metadata_file_path: Path = connector_directory / "metadata.yaml" try: diff --git a/airbyte_cdk/test/models/scenario.py b/airbyte_cdk/test/models/scenario.py index 033807ef7..2704f6e96 100644 --- a/airbyte_cdk/test/models/scenario.py +++ b/airbyte_cdk/test/models/scenario.py @@ -186,3 +186,8 @@ def with_expecting_success(self) -> ConnectorTestScenario: **self.model_dump(exclude={"status"}), status="succeed", ) + + @property + def requires_creds(self) -> bool: + """Return True if the scenario requires credentials to run.""" + return bool(self.config_path and "secrets" in self.config_path.parts) diff --git a/airbyte_cdk/test/standard_tests/pytest_hooks.py b/airbyte_cdk/test/standard_tests/pytest_hooks.py index b267bd7a3..cae8f1abb 100644 --- a/airbyte_cdk/test/standard_tests/pytest_hooks.py +++ b/airbyte_cdk/test/standard_tests/pytest_hooks.py @@ -161,7 +161,7 @@ class TestMyConnector(ConnectorTestSuiteBase): if test_class is None: return - # Get the 'scenarios' attribute from the class + # Check that the class is compatible with our test suite scenarios_attr = getattr(test_class, "get_scenarios", None) if scenarios_attr is None: raise ValueError( @@ -169,6 +169,21 @@ class TestMyConnector(ConnectorTestSuiteBase): "Please define the 'scenarios' attribute in the test class." ) + # Get the scenarios defined or discovered in the test class scenarios = test_class.get_scenarios() - ids = [str(scenario) for scenario in scenarios] - metafunc.parametrize("scenario", scenarios, ids=ids) + + # Create pytest.param objects with special marks as needed + parametrized_scenarios = [ + pytest.param( + scenario, + marks=[pytest.mark.requires_creds] if scenario.requires_creds else [], + ) + for scenario in scenarios + ] + + # Parametrize the 'scenario' argument with the scenarios + metafunc.parametrize( + "scenario", + parametrized_scenarios, + ids=[str(scenario) for scenario in scenarios], + )