From af31917294eb7ad0458a3bab9c0cda9aa37e0f11 Mon Sep 17 00:00:00 2001 From: rpkay Date: Thu, 16 Nov 2023 13:04:06 +0530 Subject: [PATCH 1/3] test case for docker exceptions --- tests/providers/docker/test_exceptions.py | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/providers/docker/test_exceptions.py diff --git a/tests/providers/docker/test_exceptions.py b/tests/providers/docker/test_exceptions.py new file mode 100644 index 0000000000000..4fbc4694c55c0 --- /dev/null +++ b/tests/providers/docker/test_exceptions.py @@ -0,0 +1,46 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +"""Test for Exceptions used by Docker provider.""" +from __future__ import annotations + +import pytest + +from airflow.providers.docker.exceptions import ( + DockerContainerFailedException, + DockerContainerFailedSkipException, +) + +FAILED_MESSAGE = "Error 1" +FAILED_LOGS = ["Log 1", "Log2"] +FAILED_SKIP_MESSAGE = "Error 2" +FAILED_SKIP_LOGS = ["Log 3", "Log4"] + + +class TestDockerContainerExceptions: + @pytest.mark.parametrize( + "exception_class, message, logs", + [ + (DockerContainerFailedException, FAILED_MESSAGE, FAILED_LOGS), + (DockerContainerFailedSkipException, FAILED_SKIP_MESSAGE, FAILED_SKIP_LOGS), + ], + ) + def test_docker_failed_exception(self, exception_class, message, logs): + with pytest.raises(exception_class) as exc_info: + raise exception_class(message, logs) + assert exc_info.value.args[0] == message + assert exc_info.value.logs == logs From f9cd141fe5bdffe52a0455e41443f518f2a38b80 Mon Sep 17 00:00:00 2001 From: rpkay Date: Fri, 17 Nov 2023 21:26:00 +0530 Subject: [PATCH 2/3] removed docker test exception --- tests/always/test_project_structure.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/always/test_project_structure.py b/tests/always/test_project_structure.py index a58f33d7556c4..967c3ecbb5be6 100644 --- a/tests/always/test_project_structure.py +++ b/tests/always/test_project_structure.py @@ -108,7 +108,6 @@ def test_providers_modules_should_have_tests(self): "tests/providers/cncf/kubernetes/utils/test_xcom_sidecar.py", "tests/providers/daskexecutor/executors/test_dask_executor.py", "tests/providers/databricks/hooks/test_databricks_base.py", - "tests/providers/docker/test_exceptions.py", "tests/providers/google/cloud/fs/test_gcs.py", "tests/providers/google/cloud/links/test_automl.py", "tests/providers/google/cloud/links/test_base.py", From 95a5cb2d085a549d75015bf8a72ef22ce30b5845 Mon Sep 17 00:00:00 2001 From: rpkay Date: Fri, 17 Nov 2023 21:26:34 +0530 Subject: [PATCH 3/3] re-wrote test case based on feedback --- tests/providers/docker/test_exceptions.py | 66 +++++++++++++++++------ 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/tests/providers/docker/test_exceptions.py b/tests/providers/docker/test_exceptions.py index 4fbc4694c55c0..1f3653c39dcd8 100644 --- a/tests/providers/docker/test_exceptions.py +++ b/tests/providers/docker/test_exceptions.py @@ -16,31 +16,65 @@ # specific language governing permissions and limitations # under the License. """Test for Exceptions used by Docker provider.""" + from __future__ import annotations +from unittest import mock + import pytest +from docker import APIClient from airflow.providers.docker.exceptions import ( DockerContainerFailedException, DockerContainerFailedSkipException, ) +from airflow.providers.docker.operators.docker import DockerOperator -FAILED_MESSAGE = "Error 1" -FAILED_LOGS = ["Log 1", "Log2"] -FAILED_SKIP_MESSAGE = "Error 2" -FAILED_SKIP_LOGS = ["Log 3", "Log4"] +FAILED_MESSAGE = {"StatusCode": 1} +FAILED_LOGS = ["unicode container log 😁 ", b"byte string container log"] +EXPECTED_MESSAGE = f"Docker container failed: {FAILED_MESSAGE}" +FAILED_SKIP_MESSAGE = {"StatusCode": 2} +SKIP_ON_EXIT_CODE = 2 +EXPECTED_SKIP_MESSAGE = f"Docker container returned exit code {[SKIP_ON_EXIT_CODE]}. Skipping." +@pytest.mark.parametrize( + "failed_msg, log_line, expected_message, skip_on_exit_code", + [ + (FAILED_MESSAGE, FAILED_LOGS, EXPECTED_MESSAGE, None), + (FAILED_SKIP_MESSAGE, FAILED_LOGS, EXPECTED_SKIP_MESSAGE, SKIP_ON_EXIT_CODE), + ], +) class TestDockerContainerExceptions: - @pytest.mark.parametrize( - "exception_class, message, logs", - [ - (DockerContainerFailedException, FAILED_MESSAGE, FAILED_LOGS), - (DockerContainerFailedSkipException, FAILED_SKIP_MESSAGE, FAILED_SKIP_LOGS), - ], - ) - def test_docker_failed_exception(self, exception_class, message, logs): - with pytest.raises(exception_class) as exc_info: - raise exception_class(message, logs) - assert exc_info.value.args[0] == message - assert exc_info.value.logs == logs + @pytest.fixture(autouse=True, scope="function") + def setup_patchers(self, docker_api_client_patcher): + self.client_mock = mock.MagicMock(spec=APIClient) + self.client_mock.wait.return_value = {"StatusCode": 0} + self.log_messages = ["container log 😁 ", b"byte string container log"] + self.client_mock.attach.return_value = self.log_messages + + self.client_mock.logs.side_effect = ( + lambda **kwargs: iter(self.log_messages[-kwargs["tail"] :]) + if "tail" in kwargs + else iter(self.log_messages) + ) + + docker_api_client_patcher.return_value = self.client_mock + + def test_docker_failed_exception(self, failed_msg, log_line, expected_message, skip_on_exit_code): + self.client_mock.attach.return_value = log_line + self.client_mock.wait.return_value = failed_msg + + operator = DockerOperator( + image="ubuntu", owner="unittest", task_id="unittest", skip_on_exit_code=skip_on_exit_code + ) + + if skip_on_exit_code: + with pytest.raises(DockerContainerFailedSkipException) as raised_exception: + operator.execute(None) + else: + with pytest.raises(DockerContainerFailedException) as raised_exception: + operator.execute(None) + + assert str(raised_exception.value) == expected_message + assert raised_exception.value.logs == [log_line[0].strip(), log_line[1].decode("utf-8")]