mirror of
https://github.com/ansible-collections/community.docker.git
synced 2026-07-29 11:55:04 +00:00
Add typing information, 2/n (#1178)
* Add typing to Docker Stack modules. Clean modules up. * Add typing to Docker Swarm modules. * Add typing to unit tests. * Add more typing. * Add ignore.txt entries.
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
|
||||
import pytest
|
||||
|
||||
from ansible_collections.community.docker.plugins.modules.docker_container_copy_into import (
|
||||
@@ -30,7 +32,7 @@ from ansible_collections.community.docker.plugins.modules.docker_container_copy_
|
||||
("-1", -1),
|
||||
],
|
||||
)
|
||||
def test_parse_string(value, expected):
|
||||
def test_parse_string(value: str, expected: int) -> None:
|
||||
assert parse_modern(value) == expected
|
||||
assert parse_octal_string_only(value) == expected
|
||||
|
||||
@@ -45,10 +47,10 @@ def test_parse_string(value, expected):
|
||||
123456789012345678901234567890123456789012345678901234567890,
|
||||
],
|
||||
)
|
||||
def test_parse_int(value):
|
||||
def test_parse_int(value: int) -> None:
|
||||
assert parse_modern(value) == value
|
||||
with pytest.raises(TypeError, match=f"^must be an octal string, got {value}L?$"):
|
||||
parse_octal_string_only(value)
|
||||
parse_octal_string_only(value) # type: ignore
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -60,7 +62,7 @@ def test_parse_int(value):
|
||||
{},
|
||||
],
|
||||
)
|
||||
def test_parse_bad_type(value):
|
||||
def test_parse_bad_type(value: t.Any) -> None:
|
||||
with pytest.raises(TypeError, match="^must be an octal string or an integer, got "):
|
||||
parse_modern(value)
|
||||
with pytest.raises(TypeError, match="^must be an octal string, got "):
|
||||
@@ -75,7 +77,7 @@ def test_parse_bad_type(value):
|
||||
"9",
|
||||
],
|
||||
)
|
||||
def test_parse_bad_value(value):
|
||||
def test_parse_bad_value(value: str) -> None:
|
||||
with pytest.raises(ValueError):
|
||||
parse_modern(value)
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
|
||||
import pytest
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils._image_archive import (
|
||||
@@ -19,19 +21,24 @@ from ..test_support.docker_image_archive_stubbing import (
|
||||
)
|
||||
|
||||
|
||||
def assert_no_logging(msg):
|
||||
if t.TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def assert_no_logging(msg: str) -> t.NoReturn:
|
||||
raise AssertionError(f"Should not have logged anything but logged {msg}")
|
||||
|
||||
|
||||
def capture_logging(messages):
|
||||
def capture(msg):
|
||||
def capture_logging(messages: list[str]) -> Callable[[str], None]:
|
||||
def capture(msg: str) -> None:
|
||||
messages.append(msg)
|
||||
|
||||
return capture
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tar_file_name(tmpdir):
|
||||
def tar_file_name(tmpdir: t.Any) -> str:
|
||||
"""
|
||||
Return the name of a non-existing tar file in an existing temporary directory.
|
||||
"""
|
||||
@@ -39,7 +46,7 @@ def tar_file_name(tmpdir):
|
||||
return tmpdir.join("foo.tar")
|
||||
|
||||
|
||||
def test_archived_image_action_when_missing(tar_file_name):
|
||||
def test_archived_image_action_when_missing(tar_file_name: str) -> None:
|
||||
fake_name = "a:latest"
|
||||
fake_id = "a1"
|
||||
|
||||
@@ -52,7 +59,7 @@ def test_archived_image_action_when_missing(tar_file_name):
|
||||
assert actual == expected
|
||||
|
||||
|
||||
def test_archived_image_action_when_current(tar_file_name):
|
||||
def test_archived_image_action_when_current(tar_file_name: str) -> None:
|
||||
fake_name = "b:latest"
|
||||
fake_id = "b2"
|
||||
|
||||
@@ -65,7 +72,7 @@ def test_archived_image_action_when_current(tar_file_name):
|
||||
assert actual is None
|
||||
|
||||
|
||||
def test_archived_image_action_when_invalid(tar_file_name):
|
||||
def test_archived_image_action_when_invalid(tar_file_name: str) -> None:
|
||||
fake_name = "c:1.2.3"
|
||||
fake_id = "c3"
|
||||
|
||||
@@ -73,7 +80,7 @@ def test_archived_image_action_when_invalid(tar_file_name):
|
||||
|
||||
expected = f"Archived image {fake_name} to {tar_file_name}, overwriting an unreadable archive file"
|
||||
|
||||
actual_log = []
|
||||
actual_log: list[str] = []
|
||||
actual = ImageManager.archived_image_action(
|
||||
capture_logging(actual_log), tar_file_name, fake_name, api_image_id(fake_id)
|
||||
)
|
||||
@@ -84,7 +91,7 @@ def test_archived_image_action_when_invalid(tar_file_name):
|
||||
assert actual_log[0].startswith("Unable to extract manifest summary from archive")
|
||||
|
||||
|
||||
def test_archived_image_action_when_obsolete_by_id(tar_file_name):
|
||||
def test_archived_image_action_when_obsolete_by_id(tar_file_name: str) -> None:
|
||||
fake_name = "d:0.0.1"
|
||||
old_id = "e5"
|
||||
new_id = "d4"
|
||||
@@ -99,7 +106,7 @@ def test_archived_image_action_when_obsolete_by_id(tar_file_name):
|
||||
assert actual == expected
|
||||
|
||||
|
||||
def test_archived_image_action_when_obsolete_by_name(tar_file_name):
|
||||
def test_archived_image_action_when_obsolete_by_name(tar_file_name: str) -> None:
|
||||
old_name = "hi"
|
||||
new_name = "d:0.0.1"
|
||||
fake_id = "d4"
|
||||
|
||||
@@ -21,5 +21,5 @@ from ansible_collections.community.docker.plugins.modules.docker_image_build imp
|
||||
('\rhello, "hi" !\n', '"\rhello, ""hi"" !\n"'),
|
||||
],
|
||||
)
|
||||
def test__quote_csv(value, expected):
|
||||
def test__quote_csv(value: str, expected: str) -> None:
|
||||
assert _quote_csv(value) == expected
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
|
||||
import pytest
|
||||
|
||||
from ansible_collections.community.docker.plugins.modules.docker_network import (
|
||||
@@ -23,7 +25,9 @@ from ansible_collections.community.docker.plugins.modules.docker_network import
|
||||
("fdd1:ac8c:0557:7ce2::/128", "ipv6"),
|
||||
],
|
||||
)
|
||||
def test_validate_cidr_positives(cidr, expected):
|
||||
def test_validate_cidr_positives(
|
||||
cidr: str, expected: t.Literal["ipv4", "ipv6"]
|
||||
) -> None:
|
||||
assert validate_cidr(cidr) == expected
|
||||
|
||||
|
||||
@@ -36,7 +40,7 @@ def test_validate_cidr_positives(cidr, expected):
|
||||
"fdd1:ac8c:0557:7ce2::",
|
||||
],
|
||||
)
|
||||
def test_validate_cidr_negatives(cidr):
|
||||
def test_validate_cidr_negatives(cidr: str) -> None:
|
||||
with pytest.raises(ValueError) as e:
|
||||
validate_cidr(cidr)
|
||||
assert f'"{cidr}" is not a valid CIDR' == str(e.value)
|
||||
|
||||
@@ -4,66 +4,47 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class APIErrorMock(Exception):
|
||||
def __init__(self, message, response=None, explanation=None):
|
||||
self.message = message
|
||||
self.response = response
|
||||
self.explanation = explanation
|
||||
from ansible_collections.community.docker.plugins.modules import (
|
||||
docker_swarm_service,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def docker_module_mock(mocker):
|
||||
docker_module_mock = mocker.MagicMock()
|
||||
docker_utils_module_mock = mocker.MagicMock()
|
||||
docker_errors_module_mock = mocker.MagicMock()
|
||||
docker_errors_module_mock.APIError = APIErrorMock
|
||||
mock_modules = {
|
||||
"docker": docker_module_mock,
|
||||
"docker.utils": docker_utils_module_mock,
|
||||
"docker.errors": docker_errors_module_mock,
|
||||
}
|
||||
return mocker.patch.dict("sys.modules", **mock_modules)
|
||||
APIError = pytest.importorskip("docker.errors.APIError")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def docker_swarm_service():
|
||||
from ansible_collections.community.docker.plugins.modules import (
|
||||
docker_swarm_service,
|
||||
)
|
||||
|
||||
return docker_swarm_service
|
||||
|
||||
|
||||
def test_retry_on_out_of_sequence_error(mocker, docker_swarm_service):
|
||||
def test_retry_on_out_of_sequence_error(mocker: t.Any) -> None:
|
||||
run_mock = mocker.MagicMock(
|
||||
side_effect=APIErrorMock(
|
||||
side_effect=APIError(
|
||||
message="",
|
||||
response=None,
|
||||
explanation="rpc error: code = Unknown desc = update out of sequence",
|
||||
)
|
||||
)
|
||||
manager = docker_swarm_service.DockerServiceManager(client=None)
|
||||
manager.run = run_mock
|
||||
with pytest.raises(APIErrorMock):
|
||||
mocker.patch("time.sleep")
|
||||
manager = docker_swarm_service.DockerServiceManager(client=None) # type: ignore
|
||||
manager.run = run_mock # type: ignore
|
||||
with pytest.raises(APIError):
|
||||
manager.run_safe()
|
||||
assert run_mock.call_count == 3
|
||||
|
||||
|
||||
def test_no_retry_on_general_api_error(mocker, docker_swarm_service):
|
||||
def test_no_retry_on_general_api_error(mocker: t.Any) -> None:
|
||||
run_mock = mocker.MagicMock(
|
||||
side_effect=APIErrorMock(message="", response=None, explanation="some error")
|
||||
side_effect=APIError(message="", response=None, explanation="some error")
|
||||
)
|
||||
manager = docker_swarm_service.DockerServiceManager(client=None)
|
||||
manager.run = run_mock
|
||||
with pytest.raises(APIErrorMock):
|
||||
mocker.patch("time.sleep")
|
||||
manager = docker_swarm_service.DockerServiceManager(client=None) # type: ignore
|
||||
manager.run = run_mock # type: ignore
|
||||
with pytest.raises(APIError):
|
||||
manager.run_safe()
|
||||
assert run_mock.call_count == 1
|
||||
|
||||
|
||||
def test_get_docker_environment(mocker, docker_swarm_service):
|
||||
def test_get_docker_environment(mocker: t.Any) -> None:
|
||||
env_file_result = {"TEST1": "A", "TEST2": "B", "TEST3": "C"}
|
||||
env_dict = {"TEST3": "CC", "TEST4": "D"}
|
||||
env_string = "TEST3=CC,TEST4=D"
|
||||
@@ -103,7 +84,7 @@ def test_get_docker_environment(mocker, docker_swarm_service):
|
||||
assert result == []
|
||||
|
||||
|
||||
def test_get_nanoseconds_from_raw_option(docker_swarm_service):
|
||||
def test_get_nanoseconds_from_raw_option() -> None:
|
||||
value = docker_swarm_service.get_nanoseconds_from_raw_option("test", None)
|
||||
assert value is None
|
||||
|
||||
@@ -117,7 +98,7 @@ def test_get_nanoseconds_from_raw_option(docker_swarm_service):
|
||||
docker_swarm_service.get_nanoseconds_from_raw_option("test", [])
|
||||
|
||||
|
||||
def test_has_dict_changed(docker_swarm_service):
|
||||
def test_has_dict_changed() -> None:
|
||||
assert not docker_swarm_service.has_dict_changed(
|
||||
{"a": 1},
|
||||
{"a": 1},
|
||||
@@ -135,8 +116,7 @@ def test_has_dict_changed(docker_swarm_service):
|
||||
assert not docker_swarm_service.has_dict_changed(None, {})
|
||||
|
||||
|
||||
def test_has_list_changed(docker_swarm_service):
|
||||
|
||||
def test_has_list_changed() -> None:
|
||||
# List comparisons without dictionaries
|
||||
# I could improve the indenting, but pycodestyle wants this instead
|
||||
assert not docker_swarm_service.has_list_changed(None, None)
|
||||
@@ -161,7 +141,7 @@ def test_has_list_changed(docker_swarm_service):
|
||||
assert docker_swarm_service.has_list_changed([None, 1], [2, 1])
|
||||
assert docker_swarm_service.has_list_changed([2, 1], [None, 1])
|
||||
assert docker_swarm_service.has_list_changed(
|
||||
"command --with args", ["command", "--with", "args"]
|
||||
["command --with args"], ["command", "--with", "args"]
|
||||
)
|
||||
assert docker_swarm_service.has_list_changed(
|
||||
["sleep", "3400"], ["sleep", "3600"], sort_lists=False
|
||||
@@ -259,7 +239,7 @@ def test_has_list_changed(docker_swarm_service):
|
||||
)
|
||||
|
||||
|
||||
def test_have_networks_changed(docker_swarm_service):
|
||||
def test_have_networks_changed() -> None:
|
||||
assert not docker_swarm_service.have_networks_changed(None, None)
|
||||
|
||||
assert not docker_swarm_service.have_networks_changed([], None)
|
||||
@@ -329,14 +309,14 @@ def test_have_networks_changed(docker_swarm_service):
|
||||
)
|
||||
|
||||
|
||||
def test_get_docker_networks(docker_swarm_service):
|
||||
def test_get_docker_networks() -> None:
|
||||
network_names = [
|
||||
"network_1",
|
||||
"network_2",
|
||||
"network_3",
|
||||
"network_4",
|
||||
]
|
||||
networks = [
|
||||
networks: list[str | dict[str, t.Any]] = [
|
||||
network_names[0],
|
||||
{"name": network_names[1]},
|
||||
{"name": network_names[2], "aliases": ["networkalias1"]},
|
||||
@@ -367,28 +347,27 @@ def test_get_docker_networks(docker_swarm_service):
|
||||
assert "foo" in network["options"]
|
||||
# Test missing name
|
||||
with pytest.raises(TypeError):
|
||||
docker_swarm_service.get_docker_networks([{"invalid": "err"}], {"err": 1})
|
||||
docker_swarm_service.get_docker_networks([{"invalid": "err"}], {"err": "x"})
|
||||
# test for invalid aliases type
|
||||
with pytest.raises(TypeError):
|
||||
docker_swarm_service.get_docker_networks(
|
||||
[{"name": "test", "aliases": 1}], {"test": 1}
|
||||
[{"name": "test", "aliases": 1}], {"test": "x"}
|
||||
)
|
||||
# Test invalid aliases elements
|
||||
with pytest.raises(TypeError):
|
||||
docker_swarm_service.get_docker_networks(
|
||||
[{"name": "test", "aliases": [1]}], {"test": 1}
|
||||
[{"name": "test", "aliases": [1]}], {"test": "x"}
|
||||
)
|
||||
# Test for invalid options type
|
||||
with pytest.raises(TypeError):
|
||||
docker_swarm_service.get_docker_networks(
|
||||
[{"name": "test", "options": 1}], {"test": 1}
|
||||
[{"name": "test", "options": 1}], {"test": "x"}
|
||||
)
|
||||
# Test for invalid networks type
|
||||
with pytest.raises(TypeError):
|
||||
docker_swarm_service.get_docker_networks(1, {"test": 1})
|
||||
# Test for non existing networks
|
||||
with pytest.raises(ValueError):
|
||||
docker_swarm_service.get_docker_networks([{"name": "idontexist"}], {"test": 1})
|
||||
docker_swarm_service.get_docker_networks(
|
||||
[{"name": "idontexist"}], {"test": "x"}
|
||||
)
|
||||
# Test empty values
|
||||
assert docker_swarm_service.get_docker_networks([], {}) == []
|
||||
assert docker_swarm_service.get_docker_networks(None, {}) is None
|
||||
|
||||
Reference in New Issue
Block a user