Skip to content

Commit

Permalink
Add missing test for kubernetes compat converters (apache#43248)
Browse files Browse the repository at this point in the history
  • Loading branch information
yangyulely authored Nov 1, 2024
1 parent 199d9ac commit 2c67c9f
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 1 deletion.
16 changes: 16 additions & 0 deletions providers/tests/cncf/kubernetes/backcompat/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
# 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.
from __future__ import annotations

from unittest.mock import Mock, patch

import pytest
from kubernetes.client import models as k8s

from airflow.exceptions import AirflowException
from airflow.providers.cncf.kubernetes.backcompat.backwards_compat_converters import (
_convert_from_dict,
_convert_kube_model_object,
convert_affinity,
convert_configmap,
convert_env_vars,
convert_env_vars_or_raise_error,
convert_image_pull_secrets,
convert_pod_runtime_info_env,
convert_port,
convert_toleration,
convert_volume,
convert_volume_mount,
)


# testcase of _convert_kube_model_object() function
class MockKubeModelObject:
def to_k8s_client_obj(self):
return "converted_object"


def test__convert_kube_model_object_normal_value():
obj = MockKubeModelObject()
new_class = type(obj)

result = _convert_kube_model_object(obj, new_class)
assert result == "converted_object"


def test__convert_kube_model_object_already_instance():
result = _convert_kube_model_object("obj", str)
assert result == "obj"


def test__convert_kube_model_object_invalid_type():
obj = "obj"
with pytest.raises(AirflowException) as exc_info:
_convert_kube_model_object(obj, int)

assert str(exc_info.value) == f"Expected {int}, got {type(obj)}"


# testcase of _convert_from_dict() function
@pytest.fixture
def mock_api_client():
with patch(
"airflow.providers.cncf.kubernetes.backcompat.backwards_compat_converters.ApiClient"
) as mock_class:
instance = mock_class.return_value
instance._ApiClient__deserialize_model = Mock(return_value="mocked_instance")
yield instance


def test_convert_from_dict_with_new_class_instance(mock_api_client):
obj = Mock()
result = _convert_from_dict(obj, type(obj))

assert result == obj


def test_convert_from_dict_with_dict(mock_api_client):
obj = {"key": "value"}
new_class = Mock()
result = _convert_from_dict(obj, type(new_class))

mock_api_client._ApiClient__deserialize_model.assert_called_once_with(obj, type(new_class))
assert result == "mocked_instance"


def test_convert_from_dict_with_invalid_type():
obj = "not a dict"
new_class = Mock()

with pytest.raises(AirflowException) as exc_info:
_convert_from_dict(obj, type(new_class))

assert str(exc_info.value) == "Expected dict or <class 'unittest.mock.Mock'>, got <class 'str'>"


# testcase of convert_volume() function
@patch("airflow.providers.cncf.kubernetes.backcompat.backwards_compat_converters._convert_kube_model_object")
def test_convert_volume_normal_value(mock_convert_kube_model_object):
mock_convert_kube_model_object.return_value = k8s.V1Volume(name="test_convert_volume")
volume = Mock()
result = convert_volume(volume)

mock_convert_kube_model_object.assert_called_once_with(volume, k8s.V1Volume)
assert result.name == "test_convert_volume"


# testcase of convert_volume_mount() function
@patch("airflow.providers.cncf.kubernetes.backcompat.backwards_compat_converters._convert_kube_model_object")
def test_convert_volume_mount_normal_value(mock_convert_kube_model_object):
mock_convert_kube_model_object.return_value = k8s.V1VolumeMount(
name="test_volume_mount", mount_path="/mnt/test"
)
volume = Mock()
result = convert_volume_mount(volume)

mock_convert_kube_model_object.assert_called_once_with(volume, k8s.V1VolumeMount)
assert result.name == "test_volume_mount"
assert result.mount_path == "/mnt/test"


# testcase of convert_port() function
@patch("airflow.providers.cncf.kubernetes.backcompat.backwards_compat_converters._convert_kube_model_object")
def test_convert_port_normal_value(mock_convert_kube_model_object):
mock_convert_kube_model_object.return_value = k8s.V1ContainerPort(container_port=80)
volume = Mock()
result = convert_port(volume)

mock_convert_kube_model_object.assert_called_once_with(volume, k8s.V1ContainerPort)
assert result.container_port == 80


# testcase of convert_env_vars() function
def test_convert_env_vars_with_dict():
# Normal value input case test
env_vars = {"FOO": "bar", "BAZ": "qux"}
result = convert_env_vars(env_vars)
expected_result = [k8s.V1EnvVar(name="FOO", value="bar"), k8s.V1EnvVar(name="BAZ", value="qux")]

assert isinstance(result, list)
assert len(result) == 2
assert result == expected_result


def test_convert_env_vars_with_list():
# Normal value input case test
env_vars = [k8s.V1EnvVar(name="FOO", value="bar"), k8s.V1EnvVar(name="BAZ", value="qux")]

result = convert_env_vars(env_vars)
assert result == env_vars


# testcase of convert_env_vars_or_raise_error() function
def test_convert_env_vars_or_raise_error_normal_value():
env_vars_dict = {"ENV1": "value1", "ENV2": "value2"}
result = convert_env_vars_or_raise_error(env_vars_dict)

assert isinstance(result, list)
assert len(result) == 2
assert result[0].name == "ENV1"
assert result[0].value == "value1"
assert result[1].name == "ENV2"
assert result[1].value == "value2"


def test_convert_env_vars_or_raise_error_list_value():
env_vars_list = [k8s.V1EnvVar(name="ENV1", value="value1"), k8s.V1EnvVar(name="ENV2", value="value2")]
result = convert_env_vars_or_raise_error(env_vars_list)

assert isinstance(result, list)
assert len(result) == 2
assert result[0].name == "ENV1"
assert result[0].value == "value1"
assert result[1].name == "ENV2"
assert result[1].value == "value2"
assert result == env_vars_list


def test_convert_env_vars_or_raise_error_invalid_type():
invalid_input = 123
with pytest.raises(AirflowException) as exc_info:
convert_env_vars_or_raise_error(invalid_input)

assert str(exc_info.value) == f"Expected dict or list, got {type(invalid_input)}"


# testcase of convert_pod_runtime_info_env() function
@patch("airflow.providers.cncf.kubernetes.backcompat.backwards_compat_converters._convert_kube_model_object")
def test_convert_pod_runtime_info_env_normal_value(mock_convert_kube_model_object):
mock_convert_kube_model_object.return_value = k8s.V1EnvVar(name="FOO", value="bar")
volume = Mock()
result = convert_pod_runtime_info_env(volume)

mock_convert_kube_model_object.assert_called_once_with(volume, k8s.V1EnvVar)
assert result.name == "FOO"
assert result.value == "bar"


# testcase of convert_image_pull_secrets() function
def test_convert_image_pull_secrets_normal_value():
image_pull_secrets = "secret1,secret2,secret3"
result = convert_image_pull_secrets(image_pull_secrets)

expected_result = [
k8s.V1LocalObjectReference(name="secret1"),
k8s.V1LocalObjectReference(name="secret2"),
k8s.V1LocalObjectReference(name="secret3"),
]
assert isinstance(result, list)
assert len(result) == 3
assert result == expected_result


def test_convert_image_pull_secrets_not_string():
image_pull_secrets = ["single_secret"]
result = convert_image_pull_secrets(image_pull_secrets)

assert isinstance(result, list)
assert len(result) == 1
assert result == image_pull_secrets


# testcase of convert_configmap() function
def test_convert_configmap_normal_value():
configmaps = "test-configmap"
result = convert_configmap(configmaps)

assert isinstance(result, k8s.V1EnvFromSource)
assert result.config_map_ref.name == "test-configmap"


# testcase of convert_affinity() function
@patch("airflow.providers.cncf.kubernetes.backcompat.backwards_compat_converters._convert_from_dict")
def test_convert_affinity_normal_value(mock_convert_from_dict):
affinity = {"some_key": "some_value"}
expected_result = Mock(k8s.V1Affinity)
mock_convert_from_dict.return_value = expected_result

result = convert_affinity(affinity)

mock_convert_from_dict.assert_called_once_with(affinity, k8s.V1Affinity)
assert result == expected_result


# testcase of convert_toleration() function
@patch("airflow.providers.cncf.kubernetes.backcompat.backwards_compat_converters._convert_from_dict")
def test_convert_toleration_normal_value(mock_convert_from_dict):
toleration = {
"key": "key",
"operator": "Equal",
"value": "value",
"effect": "NoExecute",
"toleration_seconds": 600,
}
expected_result = Mock(spec=k8s.V1Toleration)
mock_convert_from_dict.return_value = expected_result

result = convert_toleration(toleration)

mock_convert_from_dict.assert_called_once_with(toleration, k8s.V1Toleration)
assert isinstance(result, k8s.V1Toleration)
assert result == expected_result
1 change: 0 additions & 1 deletion tests/always/test_project_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ def test_providers_modules_should_have_tests(self):
"providers/tests/celery/executors/test_celery_executor_utils.py",
"providers/tests/celery/executors/test_default_celery.py",
"providers/tests/cloudant/test_cloudant_fake.py",
"providers/tests/cncf/kubernetes/backcompat/test_backwards_compat_converters.py",
"providers/tests/cncf/kubernetes/executors/test_kubernetes_executor_types.py",
"providers/tests/cncf/kubernetes/executors/test_kubernetes_executor_utils.py",
"providers/tests/cncf/kubernetes/operators/test_kubernetes_pod.py",
Expand Down

0 comments on commit 2c67c9f

Please sign in to comment.