From 8a754e0858d922e955e71b253c139e071ecec432 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 18:04:21 +0200 Subject: Adding upstream version 2.14.3. Signed-off-by: Daniel Baumann --- .../cli/galaxy/test_collection_extract_tar.py | 61 +++++ test/units/cli/galaxy/test_display_collection.py | 46 ++++ test/units/cli/galaxy/test_display_header.py | 41 +++ test/units/cli/galaxy/test_display_role.py | 28 ++ test/units/cli/galaxy/test_execute_list.py | 40 +++ .../cli/galaxy/test_execute_list_collection.py | 284 +++++++++++++++++++++ .../units/cli/galaxy/test_get_collection_widths.py | 34 +++ 7 files changed, 534 insertions(+) create mode 100644 test/units/cli/galaxy/test_collection_extract_tar.py create mode 100644 test/units/cli/galaxy/test_display_collection.py create mode 100644 test/units/cli/galaxy/test_display_header.py create mode 100644 test/units/cli/galaxy/test_display_role.py create mode 100644 test/units/cli/galaxy/test_execute_list.py create mode 100644 test/units/cli/galaxy/test_execute_list_collection.py create mode 100644 test/units/cli/galaxy/test_get_collection_widths.py (limited to 'test/units/cli/galaxy') diff --git a/test/units/cli/galaxy/test_collection_extract_tar.py b/test/units/cli/galaxy/test_collection_extract_tar.py new file mode 100644 index 0000000..526442c --- /dev/null +++ b/test/units/cli/galaxy/test_collection_extract_tar.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import pytest + +from ansible.errors import AnsibleError +from ansible.galaxy.collection import _extract_tar_dir + + +@pytest.fixture +def fake_tar_obj(mocker): + m_tarfile = mocker.Mock() + m_tarfile.type = mocker.Mock(return_value=b'99') + m_tarfile.SYMTYPE = mocker.Mock(return_value=b'22') + + return m_tarfile + + +def test_extract_tar_member_trailing_sep(mocker): + m_tarfile = mocker.Mock() + m_tarfile.getmember = mocker.Mock(side_effect=KeyError) + + with pytest.raises(AnsibleError, match='Unable to extract'): + _extract_tar_dir(m_tarfile, '/some/dir/', b'/some/dest') + + assert m_tarfile.getmember.call_count == 1 + + +def test_extract_tar_member_no_trailing_sep(mocker): + m_tarfile = mocker.Mock() + m_tarfile.getmember = mocker.Mock(side_effect=KeyError) + + with pytest.raises(AnsibleError, match='Unable to extract'): + _extract_tar_dir(m_tarfile, '/some/dir', b'/some/dest') + + assert m_tarfile.getmember.call_count == 2 + + +def test_extract_tar_dir_exists(mocker, fake_tar_obj): + mocker.patch('os.makedirs', return_value=None) + m_makedir = mocker.patch('os.mkdir', return_value=None) + mocker.patch('os.path.isdir', return_value=True) + + _extract_tar_dir(fake_tar_obj, '/some/dir', b'/some/dest') + + assert not m_makedir.called + + +def test_extract_tar_dir_does_not_exist(mocker, fake_tar_obj): + mocker.patch('os.makedirs', return_value=None) + m_makedir = mocker.patch('os.mkdir', return_value=None) + mocker.patch('os.path.isdir', return_value=False) + + _extract_tar_dir(fake_tar_obj, '/some/dir', b'/some/dest') + + assert m_makedir.called + assert m_makedir.call_args[0] == (b'/some/dir', 0o0755) diff --git a/test/units/cli/galaxy/test_display_collection.py b/test/units/cli/galaxy/test_display_collection.py new file mode 100644 index 0000000..c86227b --- /dev/null +++ b/test/units/cli/galaxy/test_display_collection.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import pytest + +from ansible.cli.galaxy import _display_collection +from ansible.galaxy.dependency_resolution.dataclasses import Requirement + + +@pytest.fixture +def collection_object(): + def _cobj(fqcn='sandwiches.ham'): + return Requirement(fqcn, '1.5.0', None, 'galaxy', None) + return _cobj + + +def test_display_collection(capsys, collection_object): + _display_collection(collection_object()) + out, err = capsys.readouterr() + + assert out == 'sandwiches.ham 1.5.0 \n' + + +def test_display_collections_small_max_widths(capsys, collection_object): + _display_collection(collection_object(), 1, 1) + out, err = capsys.readouterr() + + assert out == 'sandwiches.ham 1.5.0 \n' + + +def test_display_collections_large_max_widths(capsys, collection_object): + _display_collection(collection_object(), 20, 20) + out, err = capsys.readouterr() + + assert out == 'sandwiches.ham 1.5.0 \n' + + +def test_display_collection_small_minimum_widths(capsys, collection_object): + _display_collection(collection_object('a.b'), min_cwidth=0, min_vwidth=0) + out, err = capsys.readouterr() + + assert out == 'a.b 1.5.0 \n' diff --git a/test/units/cli/galaxy/test_display_header.py b/test/units/cli/galaxy/test_display_header.py new file mode 100644 index 0000000..ae926b0 --- /dev/null +++ b/test/units/cli/galaxy/test_display_header.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.cli.galaxy import _display_header + + +def test_display_header_default(capsys): + _display_header('/collections/path', 'h1', 'h2') + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert out_lines[0] == '' + assert out_lines[1] == '# /collections/path' + assert out_lines[2] == 'h1 h2 ' + assert out_lines[3] == '---------- -------' + + +def test_display_header_widths(capsys): + _display_header('/collections/path', 'Collection', 'Version', 18, 18) + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert out_lines[0] == '' + assert out_lines[1] == '# /collections/path' + assert out_lines[2] == 'Collection Version ' + assert out_lines[3] == '------------------ ------------------' + + +def test_display_header_small_widths(capsys): + _display_header('/collections/path', 'Col', 'Ver', 1, 1) + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert out_lines[0] == '' + assert out_lines[1] == '# /collections/path' + assert out_lines[2] == 'Col Ver' + assert out_lines[3] == '--- ---' diff --git a/test/units/cli/galaxy/test_display_role.py b/test/units/cli/galaxy/test_display_role.py new file mode 100644 index 0000000..e23a772 --- /dev/null +++ b/test/units/cli/galaxy/test_display_role.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.cli.galaxy import _display_role + + +def test_display_role(mocker, capsys): + mocked_galaxy_role = mocker.Mock(install_info=None) + mocked_galaxy_role.name = 'testrole' + _display_role(mocked_galaxy_role) + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert out_lines[0] == '- testrole, (unknown version)' + + +def test_display_role_known_version(mocker, capsys): + mocked_galaxy_role = mocker.Mock(install_info={'version': '1.0.0'}) + mocked_galaxy_role.name = 'testrole' + _display_role(mocked_galaxy_role) + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert out_lines[0] == '- testrole, 1.0.0' diff --git a/test/units/cli/galaxy/test_execute_list.py b/test/units/cli/galaxy/test_execute_list.py new file mode 100644 index 0000000..41fee0b --- /dev/null +++ b/test/units/cli/galaxy/test_execute_list.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- +# Copyright (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import pytest + +from ansible import context +from ansible.cli.galaxy import GalaxyCLI + + +def test_execute_list_role_called(mocker): + """Make sure the correct method is called for a role""" + + gc = GalaxyCLI(['ansible-galaxy', 'role', 'list']) + context.CLIARGS._store = {'type': 'role'} + execute_list_role_mock = mocker.patch('ansible.cli.galaxy.GalaxyCLI.execute_list_role', side_effect=AttributeError('raised intentionally')) + execute_list_collection_mock = mocker.patch('ansible.cli.galaxy.GalaxyCLI.execute_list_collection', side_effect=AttributeError('raised intentionally')) + with pytest.raises(AttributeError): + gc.execute_list() + + assert execute_list_role_mock.call_count == 1 + assert execute_list_collection_mock.call_count == 0 + + +def test_execute_list_collection_called(mocker): + """Make sure the correct method is called for a collection""" + + gc = GalaxyCLI(['ansible-galaxy', 'collection', 'list']) + context.CLIARGS._store = {'type': 'collection'} + execute_list_role_mock = mocker.patch('ansible.cli.galaxy.GalaxyCLI.execute_list_role', side_effect=AttributeError('raised intentionally')) + execute_list_collection_mock = mocker.patch('ansible.cli.galaxy.GalaxyCLI.execute_list_collection', side_effect=AttributeError('raised intentionally')) + with pytest.raises(AttributeError): + gc.execute_list() + + assert execute_list_role_mock.call_count == 0 + assert execute_list_collection_mock.call_count == 1 diff --git a/test/units/cli/galaxy/test_execute_list_collection.py b/test/units/cli/galaxy/test_execute_list_collection.py new file mode 100644 index 0000000..e8a834d --- /dev/null +++ b/test/units/cli/galaxy/test_execute_list_collection.py @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import pytest + +from ansible import context +from ansible.cli.galaxy import GalaxyCLI +from ansible.errors import AnsibleError, AnsibleOptionsError +from ansible.galaxy import collection +from ansible.galaxy.dependency_resolution.dataclasses import Requirement +from ansible.module_utils._text import to_native + + +def path_exists(path): + if to_native(path) == '/root/.ansible/collections/ansible_collections/sandwiches/ham': + return False + elif to_native(path) == '/usr/share/ansible/collections/ansible_collections/sandwiches/reuben': + return False + elif to_native(path) == 'nope': + return False + else: + return True + + +def isdir(path): + if to_native(path) == 'nope': + return False + else: + return True + + +def cliargs(collections_paths=None, collection_name=None): + if collections_paths is None: + collections_paths = ['~/root/.ansible/collections', '/usr/share/ansible/collections'] + + context.CLIARGS._store = { + 'collections_path': collections_paths, + 'collection': collection_name, + 'type': 'collection', + 'output_format': 'human' + } + + +@pytest.fixture +def mock_collection_objects(mocker): + mocker.patch('ansible.cli.galaxy.GalaxyCLI._resolve_path', side_effect=['/root/.ansible/collections', '/usr/share/ansible/collections']) + mocker.patch('ansible.cli.galaxy.validate_collection_path', + side_effect=['/root/.ansible/collections/ansible_collections', '/usr/share/ansible/collections/ansible_collections']) + + collection_args_1 = ( + ( + 'sandwiches.pbj', + '1.5.0', + None, + 'dir', + None, + ), + ( + 'sandwiches.reuben', + '2.5.0', + None, + 'dir', + None, + ), + ) + + collection_args_2 = ( + ( + 'sandwiches.pbj', + '1.0.0', + None, + 'dir', + None, + ), + ( + 'sandwiches.ham', + '1.0.0', + None, + 'dir', + None, + ), + ) + + collections_path_1 = [Requirement(*cargs) for cargs in collection_args_1] + collections_path_2 = [Requirement(*cargs) for cargs in collection_args_2] + + mocker.patch('ansible.cli.galaxy.find_existing_collections', side_effect=[collections_path_1, collections_path_2]) + + +@pytest.fixture +def mock_from_path(mocker): + def _from_path(collection_name='pbj'): + collection_args = { + 'sandwiches.pbj': ( + ( + 'sandwiches.pbj', + '1.5.0', + None, + 'dir', + None, + ), + ( + 'sandwiches.pbj', + '1.0.0', + None, + 'dir', + None, + ), + ), + 'sandwiches.ham': ( + ( + 'sandwiches.ham', + '1.0.0', + None, + 'dir', + None, + ), + ), + } + + from_path_objects = [Requirement(*args) for args in collection_args[collection_name]] + mocker.patch('ansible.cli.galaxy.Requirement.from_dir_path_as_unknown', side_effect=from_path_objects) + + return _from_path + + +def test_execute_list_collection_all(mocker, capsys, mock_collection_objects, tmp_path_factory): + """Test listing all collections from multiple paths""" + + cliargs() + + mocker.patch('os.path.exists', return_value=True) + mocker.patch('os.path.isdir', return_value=True) + gc = GalaxyCLI(['ansible-galaxy', 'collection', 'list']) + tmp_path = tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections') + concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(tmp_path, validate_certs=False) + gc.execute_list_collection(artifacts_manager=concrete_artifact_cm) + + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert len(out_lines) == 12 + assert out_lines[0] == '' + assert out_lines[1] == '# /root/.ansible/collections/ansible_collections' + assert out_lines[2] == 'Collection Version' + assert out_lines[3] == '----------------- -------' + assert out_lines[4] == 'sandwiches.pbj 1.5.0 ' + assert out_lines[5] == 'sandwiches.reuben 2.5.0 ' + assert out_lines[6] == '' + assert out_lines[7] == '# /usr/share/ansible/collections/ansible_collections' + assert out_lines[8] == 'Collection Version' + assert out_lines[9] == '-------------- -------' + assert out_lines[10] == 'sandwiches.ham 1.0.0 ' + assert out_lines[11] == 'sandwiches.pbj 1.0.0 ' + + +def test_execute_list_collection_specific(mocker, capsys, mock_collection_objects, mock_from_path, tmp_path_factory): + """Test listing a specific collection""" + + collection_name = 'sandwiches.ham' + mock_from_path(collection_name) + + cliargs(collection_name=collection_name) + mocker.patch('os.path.exists', path_exists) + mocker.patch('os.path.isdir', return_value=True) + mocker.patch('ansible.galaxy.collection.validate_collection_name', collection_name) + mocker.patch('ansible.cli.galaxy._get_collection_widths', return_value=(14, 5)) + + gc = GalaxyCLI(['ansible-galaxy', 'collection', 'list', collection_name]) + tmp_path = tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections') + concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(tmp_path, validate_certs=False) + gc.execute_list_collection(artifacts_manager=concrete_artifact_cm) + + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert len(out_lines) == 5 + assert out_lines[0] == '' + assert out_lines[1] == '# /usr/share/ansible/collections/ansible_collections' + assert out_lines[2] == 'Collection Version' + assert out_lines[3] == '-------------- -------' + assert out_lines[4] == 'sandwiches.ham 1.0.0 ' + + +def test_execute_list_collection_specific_duplicate(mocker, capsys, mock_collection_objects, mock_from_path, tmp_path_factory): + """Test listing a specific collection that exists at multiple paths""" + + collection_name = 'sandwiches.pbj' + mock_from_path(collection_name) + + cliargs(collection_name=collection_name) + mocker.patch('os.path.exists', path_exists) + mocker.patch('os.path.isdir', return_value=True) + mocker.patch('ansible.galaxy.collection.validate_collection_name', collection_name) + + gc = GalaxyCLI(['ansible-galaxy', 'collection', 'list', collection_name]) + tmp_path = tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections') + concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(tmp_path, validate_certs=False) + gc.execute_list_collection(artifacts_manager=concrete_artifact_cm) + + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert len(out_lines) == 10 + assert out_lines[0] == '' + assert out_lines[1] == '# /root/.ansible/collections/ansible_collections' + assert out_lines[2] == 'Collection Version' + assert out_lines[3] == '-------------- -------' + assert out_lines[4] == 'sandwiches.pbj 1.5.0 ' + assert out_lines[5] == '' + assert out_lines[6] == '# /usr/share/ansible/collections/ansible_collections' + assert out_lines[7] == 'Collection Version' + assert out_lines[8] == '-------------- -------' + assert out_lines[9] == 'sandwiches.pbj 1.0.0 ' + + +def test_execute_list_collection_specific_invalid_fqcn(mocker, tmp_path_factory): + """Test an invalid fully qualified collection name (FQCN)""" + + collection_name = 'no.good.name' + + cliargs(collection_name=collection_name) + mocker.patch('os.path.exists', return_value=True) + mocker.patch('os.path.isdir', return_value=True) + + gc = GalaxyCLI(['ansible-galaxy', 'collection', 'list', collection_name]) + tmp_path = tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections') + concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(tmp_path, validate_certs=False) + with pytest.raises(AnsibleError, match='Invalid collection name'): + gc.execute_list_collection(artifacts_manager=concrete_artifact_cm) + + +def test_execute_list_collection_no_valid_paths(mocker, capsys, tmp_path_factory): + """Test listing collections when no valid paths are given""" + + cliargs() + + mocker.patch('os.path.exists', return_value=True) + mocker.patch('os.path.isdir', return_value=False) + mocker.patch('ansible.utils.color.ANSIBLE_COLOR', False) + mocker.patch('ansible.cli.galaxy.display.columns', 79) + gc = GalaxyCLI(['ansible-galaxy', 'collection', 'list']) + + tmp_path = tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections') + concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(tmp_path, validate_certs=False) + + with pytest.raises(AnsibleOptionsError, match=r'None of the provided paths were usable.'): + gc.execute_list_collection(artifacts_manager=concrete_artifact_cm) + + out, err = capsys.readouterr() + + assert '[WARNING]: - the configured path' in err + assert 'exists, but it\nis not a directory.' in err + + +def test_execute_list_collection_one_invalid_path(mocker, capsys, mock_collection_objects, tmp_path_factory): + """Test listing all collections when one invalid path is given""" + + cliargs() + mocker.patch('os.path.exists', return_value=True) + mocker.patch('os.path.isdir', isdir) + mocker.patch('ansible.cli.galaxy.GalaxyCLI._resolve_path', side_effect=['/root/.ansible/collections', 'nope']) + mocker.patch('ansible.utils.color.ANSIBLE_COLOR', False) + + gc = GalaxyCLI(['ansible-galaxy', 'collection', 'list', '-p', 'nope']) + tmp_path = tmp_path_factory.mktemp('test-ÅÑŚÌβŁÈ Collections') + concrete_artifact_cm = collection.concrete_artifact_manager.ConcreteArtifactsManager(tmp_path, validate_certs=False) + gc.execute_list_collection(artifacts_manager=concrete_artifact_cm) + + out, err = capsys.readouterr() + out_lines = out.splitlines() + + assert out_lines[0] == '' + assert out_lines[1] == '# /root/.ansible/collections/ansible_collections' + assert out_lines[2] == 'Collection Version' + assert out_lines[3] == '----------------- -------' + assert out_lines[4] == 'sandwiches.pbj 1.5.0 ' + # Only a partial test of the output + + assert err == '[WARNING]: - the configured path nope, exists, but it is not a directory.\n' diff --git a/test/units/cli/galaxy/test_get_collection_widths.py b/test/units/cli/galaxy/test_get_collection_widths.py new file mode 100644 index 0000000..6e1cbf5 --- /dev/null +++ b/test/units/cli/galaxy/test_get_collection_widths.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import pytest + +from ansible.cli.galaxy import _get_collection_widths +from ansible.galaxy.dependency_resolution.dataclasses import Requirement + + +@pytest.fixture +def collection_objects(): + collection_ham = Requirement('sandwiches.ham', '1.5.0', None, 'galaxy', None) + + collection_pbj = Requirement('sandwiches.pbj', '2.5', None, 'galaxy', None) + + collection_reuben = Requirement('sandwiches.reuben', '4', None, 'galaxy', None) + + return [collection_ham, collection_pbj, collection_reuben] + + +def test_get_collection_widths(collection_objects): + assert _get_collection_widths(collection_objects) == (17, 5) + + +def test_get_collection_widths_single_collection(mocker): + mocked_collection = Requirement('sandwiches.club', '3.0.0', None, 'galaxy', None) + # Make this look like it is not iterable + mocker.patch('ansible.cli.galaxy.is_iterable', return_value=False) + + assert _get_collection_widths(mocked_collection) == (15, 5) -- cgit v1.2.3