From 3a774bd92dc8d9d087826ca7b82c01e58c21b017 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Thu, 11 Apr 2024 13:29:36 +0100 Subject: [PATCH] Migrate `tests/pytests/integration/modules/test_pip.py` to functional tests --- tests/pytests/functional/modules/test_pip.py | 309 ++++++++- tests/pytests/integration/modules/test_pip.py | 651 ------------------ 2 files changed, 302 insertions(+), 658 deletions(-) delete mode 100644 tests/pytests/integration/modules/test_pip.py diff --git a/tests/pytests/functional/modules/test_pip.py b/tests/pytests/functional/modules/test_pip.py index 6753b79b7de..fa2b1a6573d 100644 --- a/tests/pytests/functional/modules/test_pip.py +++ b/tests/pytests/functional/modules/test_pip.py @@ -1,8 +1,66 @@ +import os +import pathlib +import re +import shutil import sys +from contextlib import contextmanager import pytest -from tests.support.helpers import VirtualEnv +import salt.utils.platform +from salt.exceptions import CommandNotFoundError +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.helpers import VirtualEnv, patched_environ + +pytestmark = [ + pytest.mark.slow_test, + pytest.mark.requires_network, + pytest.mark.windows_whitelisted, + pytest.mark.skip_if_binaries_missing(*KNOWN_BINARY_NAMES, check_all=False), +] + + +@pytest.fixture +def venv(tmp_path): + with VirtualEnv(venv_dir=tmp_path / "the-venv") as venv: + yield venv + + +@pytest.fixture +def pip(modules): + with patched_environ( + PIP_SOURCE_DIR="", + PIP_BUILD_DIR="", + __cleanup__=[k for k in os.environ if k.startswith("PIP_")], + ): + yield modules.pip + + +def _pip_successful_install( + target, + expect=( + "irc3-plugins-test", + "pep8", + ), +): + """ + Isolate regex for extracting `successful install` message from pip + """ + + expect = set(expect) + expect_str = "|".join(expect) + + success = re.search( + r"^.*Successfully installed\s([^\n]+)(?:Clean.*)?", target, re.M | re.S + ) + + success_for = ( + re.findall(rf"({expect_str})(?:-(?:[\d\.-]))?", success.groups()[0]) + if success + else [] + ) + + return expect.issubset(set(success_for)) @pytest.mark.parametrize( @@ -20,13 +78,11 @@ from tests.support.helpers import VirtualEnv "pip>=21.0", ), ) -@pytest.mark.requires_network -@pytest.mark.slow_test -def test_list_available_packages(modules, pip_version, tmp_path): +def test_list_available_packages(pip, pip_version, tmp_path): with VirtualEnv(venv_dir=tmp_path, pip_requirement=pip_version) as virtualenv: virtualenv.install("-U", pip_version) package_name = "pep8" - available_versions = modules.pip.list_all_versions( + available_versions = pip.list_all_versions( package_name, bin_env=str(virtualenv.venv_bin_dir) ) assert available_versions @@ -41,7 +97,7 @@ def test_list_available_packages(modules, pip_version, tmp_path): "pip>=21.0", ), ) -def test_list_available_packages_with_index_url(modules, pip_version, tmp_path): +def test_list_available_packages_with_index_url(pip, pip_version, tmp_path): if sys.version_info < (3, 6) and pip_version == "pip>=21.0": pytest.skip(f"{pip_version} is not available on Py3.5") if sys.version_info >= (3, 10) and pip_version == "pip==9.0.3": @@ -49,9 +105,248 @@ def test_list_available_packages_with_index_url(modules, pip_version, tmp_path): with VirtualEnv(venv_dir=tmp_path, pip_requirement=pip_version) as virtualenv: virtualenv.install("-U", pip_version) package_name = "pep8" - available_versions = modules.pip.list_all_versions( + available_versions = pip.list_all_versions( package_name, bin_env=str(virtualenv.venv_bin_dir), index_url="https://pypi.python.org/simple", ) assert available_versions + + +def test_issue_2087_missing_pip(venv, pip, modules): + # Let's remove the pip binary + pip_bin = venv.venv_bin_dir / "pip" + site_dir = pathlib.Path( + modules.virtualenv.get_distribution_path(str(venv.venv_dir), "pip") + ) + if salt.utils.platform.is_windows(): + pip_bin = venv.venv_dir / "Scripts" / "pip.exe" + site_dir = venv.venv_dir / "lib" / "site-packages" + if not pip_bin.is_file(): + pytest.skip("Failed to find the pip binary to the test virtualenv") + pip_bin.unlink() + + # Also remove the pip dir from site-packages + # This is needed now that we're using python -m pip instead of the + # pip binary directly. python -m pip will still work even if the + # pip binary is missing + shutil.rmtree(site_dir / "pip") + + with pytest.raises(CommandNotFoundError) as exc: + pip.freeze(bin_env=str(venv.venv_dir)) + + assert str(exc.value) == "Could not find a `pip` binary" + + with pytest.raises(CommandNotFoundError) as exc: + pip.list(bin_env=str(venv.venv_dir)) + + assert str(exc.value) == "Could not find a `pip` binary" + + +def test_requirements_as_list_of_chains__cwd_set__absolute_file_path( + venv, pip, tmp_path +): + # Create a requirements file that depends on another one. + req1_filename = tmp_path / "requirements1.txt" + req1_filename.write_text("-r requirements1b.txt\n", encoding="utf-8") + req1b_filename = tmp_path / "requirements1b.txt" + req1b_filename.write_text("irc3-plugins-test\n", encoding="utf-8") + req2_filename = tmp_path / "requirements2.txt" + req2_filename.write_text("-r requirements2b.txt\n", encoding="utf-8") + req2b_filename = tmp_path / "requirements2b.txt" + req2b_filename.write_text("pep8\n", encoding="utf-8") + + ret = pip.install( + requirements=[str(req1_filename), str(req2_filename)], + bin_env=venv.venv_dir, + cwd=tmp_path, + ) + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert _pip_successful_install(ret["stdout"]) + + +def test_requirements_as_list_of_chains__cwd_not_set__absolute_file_path( + venv, pip, tmp_path +): + # Create a requirements file that depends on another one. + req1_filename = tmp_path / "requirements1.txt" + req1_filename.write_text("-r requirements1b.txt\n", encoding="utf-8") + req1b_filename = tmp_path / "requirements1b.txt" + req1b_filename.write_text("irc3-plugins-test\n", encoding="utf-8") + req2_filename = tmp_path / "requirements2.txt" + req2_filename.write_text("-r requirements2b.txt\n", encoding="utf-8") + req2b_filename = tmp_path / "requirements2b.txt" + req2b_filename.write_text("pep8\n", encoding="utf-8") + + ret = pip.install( + requirements=[str(req1_filename), str(req2_filename)], + bin_env=venv.venv_dir, + ) + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert _pip_successful_install(ret["stdout"]) + + +def test_requirements_as_list__absolute_file_path(venv, pip, tmp_path): + # Create a requirements file that depends on another one. + req1_filename = tmp_path / "requirements1.txt" + req1_filename.write_text("irc3-plugins-test\n", encoding="utf-8") + req2_filename = tmp_path / "requirements2.txt" + req2_filename.write_text("pep8\n", encoding="utf-8") + + ret = pip.install( + requirements=[str(req1_filename), str(req2_filename)], + bin_env=venv.venv_dir, + ) + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert _pip_successful_install(ret["stdout"]) + + +def test_requirements_as_list__non_absolute_file_path(venv, pip, tmp_path): + # Create a requirements file that depends on another one. + req1_filename = tmp_path / "requirements1.txt" + req1_filename.write_text("irc3-plugins-test\n", encoding="utf-8") + req2_filename = tmp_path / "requirements2.txt" + req2_filename.write_text("pep8\n", encoding="utf-8") + + ret = pip.install( + requirements=[str(req1_filename.name), str(req2_filename.name)], + bin_env=venv.venv_dir, + cwd=tmp_path, + ) + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert _pip_successful_install(ret["stdout"]) + + +def test_chained_requirements__absolute_file_path(venv, pip, tmp_path): + # Create a requirements file that depends on another one. + req1_filename = tmp_path / "requirements1.txt" + req1_filename.write_text("-r requirements2.txt\n", encoding="utf-8") + req2_filename = tmp_path / "requirements2.txt" + req2_filename.write_text("pep8\n", encoding="utf-8") + + ret = pip.install( + requirements=str(req1_filename), + bin_env=venv.venv_dir, + ) + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert "installed pep8" in ret["stdout"] + + +def test_chained_requirements__non_absolute_file_path(venv, pip, tmp_path): + # Create a requirements file that depends on another one. + req1_filename = tmp_path / "requirements1.txt" + req1_filename.write_text("-r requirements2.txt\n", encoding="utf-8") + req2_filename = tmp_path / "requirements2.txt" + req2_filename.write_text("pep8\n", encoding="utf-8") + + ret = pip.install( + requirements=str(req1_filename.name), bin_env=venv.venv_dir, cwd=tmp_path + ) + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert "installed pep8" in ret["stdout"] + + +@pytest.fixture +def installed_requirement(venv, pip): + + @contextmanager + def _install(version=None): + requirement = requirement_name = "pep8" + if version is not None: + requirement += f"=={version}" + ret = pip.install(pkgs=requirement, bin_env=venv.venv_dir) + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert "installed pep8" in ret["stdout"] + print(444, pip.freeze(bin_env=venv.venv_dir)) + yield requirement_name + + return _install + + +def test_pip_uninstall(venv, pip, installed_requirement): + with installed_requirement() as requirement: + ret = pip.uninstall(pkgs=requirement, bin_env=venv.venv_dir) + assert f"uninstalled {requirement}" in ret["stdout"] + + +def test_pip_install_upgrade(venv, pip, installed_requirement): + with installed_requirement(version="1.3.4") as requirement: + ret = pip.install( + pkgs=requirement, + bin_env=venv.venv_dir, + upgrade=True, + ) + assert f"installed {requirement}" in ret["stdout"] + + +def test_pip_install_multiple_editables(venv, pip): + editables = [ + "git+https://github.com/saltstack/istr.git@v1.0.1#egg=iStr", + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting", + ] + ret = pip.install(editable=editables, bin_env=venv.venv_dir) + assert _pip_successful_install(ret["stdout"], ("iStr", "SaltTesting")) + + +def test_pip_install_multiple_editables_and_pkgs(venv, pip): + editables = [ + "git+https://github.com/saltstack/istr.git@v1.0.1#egg=iStr", + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting", + ] + ret = pip.install(pkgs="pep8", editable=editables, bin_env=venv.venv_dir) + assert _pip_successful_install(ret["stdout"], ("iStr", "SaltTesting", "pep8")) + + +@pytest.mark.parametrize("touch", [True, False]) +def test_pip_non_existent_log_file(venv, pip, tmp_path, touch): + log_file = tmp_path / "tmp-pip-install.log" + if touch: + log_file.touch() + ret = pip.install(pkgs="pep8", log=str(log_file), bin_env=venv.venv_dir) + assert _pip_successful_install(ret["stdout"], ("pep8",)) + assert log_file.exists() + assert "pep8" in log_file.read_text() + + +@pytest.mark.skipif( + shutil.which("/bin/pip3") is None, reason="Could not find /bin/pip3" +) +@pytest.mark.destructive_test +@pytest.mark.skip_on_windows(reason="test specific for linux usage of /bin/python") +@pytest.mark.skip_initial_gh_actions_failure( + reason="This was skipped on older golden images and is failing on newer." +) +def test_system_pip3(pip): + pkg = "lazyimport" + pkgver = f"{pkg}==0.0.1" + ret = pip.install(pkgs=pkgver, bin_env="/bin/pip3") + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert f"installed {pkg}" in ret["stdout"] + + ret = pip.freeze(bin_env="/bin/pip3") + assert pkgver in ret + + ret = pip.uninstall(pkgs=pkg, bin_env="/bin/pip3") + assert ret + assert isinstance(ret, dict) + assert ret["retcode"] == 0 + assert f"uninstalled {pkg}" in ret["stdout"] + + ret = pip.freeze(bin_env="/bin/pip3") + assert pkg not in ret diff --git a/tests/pytests/integration/modules/test_pip.py b/tests/pytests/integration/modules/test_pip.py deleted file mode 100644 index fd9294a182c..00000000000 --- a/tests/pytests/integration/modules/test_pip.py +++ /dev/null @@ -1,651 +0,0 @@ -import os -import pprint -import re -import shutil - -import pytest - -import salt.utils.files -import salt.utils.path -import salt.utils.platform -from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES -from tests.support.helpers import VirtualEnv, patched_environ - -pytestmark = [ - pytest.mark.skip_if_binaries_missing(*KNOWN_BINARY_NAMES, check_all=False), - pytest.mark.windows_whitelisted, -] - - -@pytest.fixture(autouse=True) -def patch_environment(): - with patched_environ( - PIP_SOURCE_DIR="", - PIP_BUILD_DIR="", - __cleanup__=[k for k in os.environ if k.startswith("PIP_")], - ): - yield - - -@pytest.fixture -def venv_dir(tmp_path): - return str(tmp_path / "venv_dir") - - -def _check_download_error(ret): - """ - Checks to see if a download error looks transitory - """ - return any(w in ret for w in ["URLError", "Download error"]) - - -def _pip_successful_install( - target, - expect=( - "irc3-plugins-test", - "pep8", - ), -): - """ - isolate regex for extracting `successful install` message from pip - """ - - expect = set(expect) - expect_str = "|".join(expect) - - success = re.search( - r"^.*Successfully installed\s([^\n]+)(?:Clean.*)?", target, re.M | re.S - ) - - success_for = ( - re.findall(rf"({expect_str})(?:-(?:[\d\.-]))?", success.groups()[0]) - if success - else [] - ) - - return expect.issubset(set(success_for)) - - -@pytest.mark.slow_test -def test_issue_2087_missing_pip(venv_dir, salt_cli, salt_minion): - # Let's create the testing virtualenv - with VirtualEnv(venv_dir): - - # Let's remove the pip binary - pip_bin = os.path.join(venv_dir, "bin", "pip") - site_dir = salt_cli.run( - "virtualenv.get_distribution_path", - venv_dir, - "pip", - minion_tgt=salt_minion.id, - ).data - if salt.utils.platform.is_windows(): - pip_bin = os.path.join(venv_dir, "Scripts", "pip.exe") - site_dir = os.path.join(venv_dir, "lib", "site-packages") - if not os.path.isfile(pip_bin): - pytest.skip("Failed to find the pip binary to the test virtualenv") - os.remove(pip_bin) - - # Also remove the pip dir from site-packages - # This is needed now that we're using python -m pip instead of the - # pip binary directly. python -m pip will still work even if the - # pip binary is missing - shutil.rmtree(os.path.join(site_dir, "pip")) - - # Let's run a pip depending functions - for func in ("pip.freeze", "pip.list"): - ret = salt_cli.run(func, bin_env=venv_dir, minion_tgt=salt_minion.id).data - assert ( - "Command required for '{}' not found: Could not find a `pip` binary".format( - func - ) - in ret - ) - - -@pytest.mark.slow_test -def test_requirements_as_list_of_chains__cwd_set__absolute_file_path( - venv_dir, salt_cli, salt_minion -): - with VirtualEnv(venv_dir): - - # Create a requirements file that depends on another one. - - req1_filename = os.path.join(venv_dir, "requirements1.txt") - req1b_filename = os.path.join(venv_dir, "requirements1b.txt") - req2_filename = os.path.join(venv_dir, "requirements2.txt") - req2b_filename = os.path.join(venv_dir, "requirements2b.txt") - - with salt.utils.files.fopen(req1_filename, "w") as f: - f.write("-r requirements1b.txt\n") - with salt.utils.files.fopen(req1b_filename, "w") as f: - f.write("irc3-plugins-test\n") - with salt.utils.files.fopen(req2_filename, "w") as f: - f.write("-r requirements2b.txt\n") - with salt.utils.files.fopen(req2b_filename, "w") as f: - f.write("pep8\n") - - requirements_list = [req1_filename, req2_filename] - - ret = salt_cli.run( - "pip.install", - requirements=requirements_list, - bin_env=venv_dir, - cwd=venv_dir, - minion_tgt=salt_minion.id, - ) - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - assert ret.returncode == 0 - found = _pip_successful_install(ret.stdout) - assert found - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_requirements_as_list_of_chains__cwd_not_set__absolute_file_path( - venv_dir, salt_cli, salt_minion -): - with VirtualEnv(venv_dir): - - # Create a requirements file that depends on another one. - - req1_filename = os.path.join(venv_dir, "requirements1.txt") - req1b_filename = os.path.join(venv_dir, "requirements1b.txt") - req2_filename = os.path.join(venv_dir, "requirements2.txt") - req2b_filename = os.path.join(venv_dir, "requirements2b.txt") - - with salt.utils.files.fopen(req1_filename, "w") as f: - f.write("-r requirements1b.txt\n") - with salt.utils.files.fopen(req1b_filename, "w") as f: - f.write("irc3-plugins-test\n") - with salt.utils.files.fopen(req2_filename, "w") as f: - f.write("-r requirements2b.txt\n") - with salt.utils.files.fopen(req2b_filename, "w") as f: - f.write("pep8\n") - - requirements_list = [req1_filename, req2_filename] - - ret = salt_cli.run( - "pip.install", - requirements=requirements_list, - bin_env=venv_dir, - minion_tgt=salt_minion.id, - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - assert ret.returncode == 0 - found = _pip_successful_install(ret.stdout) - assert found - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_requirements_as_list__absolute_file_path(venv_dir, salt_cli, salt_minion): - with VirtualEnv(venv_dir): - - req1_filename = os.path.join(venv_dir, "requirements.txt") - req2_filename = os.path.join(venv_dir, "requirements2.txt") - - with salt.utils.files.fopen(req1_filename, "w") as f: - f.write("irc3-plugins-test\n") - with salt.utils.files.fopen(req2_filename, "w") as f: - f.write("pep8\n") - - requirements_list = [req1_filename, req2_filename] - - ret = salt_cli.run( - "pip.install", - requirements=requirements_list, - bin_env=venv_dir, - minion_tgt=salt_minion.id, - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - assert ret.returncode == 0 - found = _pip_successful_install(ret.stdout) - assert found - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_requirements_as_list__non_absolute_file_path(venv_dir, salt_cli, salt_minion): - with VirtualEnv(venv_dir): - - # Create a requirements file that depends on another one. - - req1_filename = "requirements.txt" - req2_filename = "requirements2.txt" - req_cwd = venv_dir - - req1_filepath = os.path.join(req_cwd, req1_filename) - req2_filepath = os.path.join(req_cwd, req2_filename) - - with salt.utils.files.fopen(req1_filepath, "w") as f: - f.write("irc3-plugins-test\n") - with salt.utils.files.fopen(req2_filepath, "w") as f: - f.write("pep8\n") - - requirements_list = [req1_filename, req2_filename] - - ret = salt_cli.run( - "pip.install", - f"cwd={req_cwd}", - requirements=requirements_list, - bin_env=venv_dir, - minion_tgt=salt_minion.id, - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - assert ret.returncode == 0 - found = _pip_successful_install(ret.stdout) - assert found - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_chained_requirements__absolute_file_path(venv_dir, salt_cli, salt_minion): - with VirtualEnv(venv_dir): - - # Create a requirements file that depends on another one. - - req1_filename = os.path.join(venv_dir, "requirements.txt") - req2_filename = os.path.join(venv_dir, "requirements2.txt") - - with salt.utils.files.fopen(req1_filename, "w") as f: - f.write("-r requirements2.txt") - with salt.utils.files.fopen(req2_filename, "w") as f: - f.write("pep8") - - ret = salt_cli.run( - "pip.install", - requirements=req1_filename, - bin_env=venv_dir, - minion_tgt=salt_minion.id, - ) - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - assert ret.returncode == 0 - assert "installed pep8" in ret.stdout - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_chained_requirements__non_absolute_file_path(venv_dir, salt_cli, salt_minion): - with VirtualEnv(venv_dir): - - # Create a requirements file that depends on another one. - req_basepath = venv_dir - - req1_filename = "requirements.txt" - req2_filename = "requirements2.txt" - - req1_file = os.path.join(venv_dir, req1_filename) - req2_file = os.path.join(venv_dir, req2_filename) - - with salt.utils.files.fopen(req1_file, "w") as f: - f.write("-r requirements2.txt") - with salt.utils.files.fopen(req2_file, "w") as f: - f.write("pep8") - - ret = salt_cli.run( - "pip.install", - f"cwd={req_basepath}", - requirements=req1_filename, - bin_env=venv_dir, - minion_tgt=salt_minion.id, - ) - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - assert ret.returncode == 0 - assert "installed pep8" in ret.stdout - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_issue_4805_nested_requirements(venv_dir, salt_cli, salt_minion): - with VirtualEnv(venv_dir): - - # Create a requirements file that depends on another one. - req1_filename = os.path.join(venv_dir, "requirements.txt") - req2_filename = os.path.join(venv_dir, "requirements2.txt") - with salt.utils.files.fopen(req1_filename, "w") as f: - f.write("-r requirements2.txt") - with salt.utils.files.fopen(req2_filename, "w") as f: - f.write("pep8") - - ret = salt_cli.run( - "pip.install", - requirements=req1_filename, - bin_env=venv_dir, - timeout=300, - minion_tgt=salt_minion.id, - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - if _check_download_error(ret.stdout): - pytest.skip("Test skipped due to pip download error") - assert ret.returncode == 0 - assert "installed pep8" in ret.stdout - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_pip_uninstall(venv_dir, salt_cli, salt_minion): - # Let's create the testing virtualenv - with VirtualEnv(venv_dir): - ret = salt_cli.run( - "pip.install", ["pep8"], bin_env=venv_dir, minion_tgt=salt_minion.id - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - if _check_download_error(ret.stdout): - pytest.skip("Test skipped due to pip download error") - assert ret.returncode == 0 - assert "installed pep8" in ret.stdout - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - ret = salt_cli.run( - "pip.uninstall", ["pep8"], bin_env=venv_dir, minion_tgt=salt_minion.id - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.uninstall' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - assert ret.returncode == 0 - assert "uninstalled pep8" in ret.stdout - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_pip_install_upgrade(venv_dir, salt_cli, salt_minion): - # Create the testing virtualenv - with VirtualEnv(venv_dir): - ret = salt_cli.run( - "pip.install", "pep8==1.3.4", bin_env=venv_dir, minion_tgt=salt_minion.id - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - if _check_download_error(ret.stdout): - pytest.skip("Test skipped due to pip download error") - assert ret.returncode == 0 - assert "installed pep8" in ret.stdout - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - ret = salt_cli.run( - "pip.install", - "pep8", - bin_env=venv_dir, - upgrade=True, - minion_tgt=salt_minion.id, - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - if _check_download_error(ret.stdout): - pytest.skip("Test skipped due to pip download error") - assert ret.returncode == 0 - assert "installed pep8" in ret.stdout - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - ret = salt_cli.run( - "pip.uninstall", "pep8", bin_env=venv_dir, minion_tgt=salt_minion.id - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.uninstall' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - assert ret.returncode == 0 - assert "uninstalled pep8" in ret.stdout - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_pip_install_multiple_editables(venv_dir, salt_cli, salt_minion): - editables = [ - "git+https://github.com/saltstack/istr.git@v1.0.1#egg=iStr", - "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting", - ] - - # Create the testing virtualenv - with VirtualEnv(venv_dir): - ret = salt_cli.run( - "pip.install", - [], - editable="{}".format(",".join(editables)), - bin_env=venv_dir, - minion_tgt=salt_minion.id, - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - if _check_download_error(ret.stdout): - pytest.skip("Test skipped due to pip download error") - assert ret.returncode == 0 - for package in ("iStr", "SaltTesting"): - match = re.search( - rf"(?:.*)(Successfully installed)(?:.*)({package})(?:.*)", - ret.stdout, - ) - assert match is not None - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.slow_test -def test_pip_install_multiple_editables_and_pkgs(venv_dir, salt_cli, salt_minion): - editables = [ - "git+https://github.com/saltstack/istr.git@v1.0.1#egg=iStr", - "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting", - ] - - # Create the testing virtualenv - with VirtualEnv(venv_dir): - ret = salt_cli.run( - "pip.install", - ["pep8"], - editable="{}".format(",".join(editables)), - bin_env=venv_dir, - minion_tgt=salt_minion.id, - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - try: - if _check_download_error(ret.stdout): - pytest.skip("Test skipped due to pip download error") - assert ret.returncode == 0 - for package in ("iStr", "SaltTesting", "pep8"): - match = re.search( - rf"(?:.*)(Successfully installed)(?:.*)({package})(?:.*)", - ret.stdout, - ) - assert match is not None - except KeyError as exc: - pytest.fail( - "The returned dictionary is missing an expected key. Error: '{}'." - " Dictionary: {}".format(exc, pprint.pformat(ret)) - ) - - -@pytest.mark.parametrize("touch", [True, False]) -@pytest.mark.slow_test -def test_pip_non_existent_log_file(venv_dir, salt_cli, salt_minion, tmp_path, touch): - log_file = tmp_path / "tmp-pip-install.log" - if touch: - log_file.touch() - # Create the testing virtualenv - with VirtualEnv(venv_dir): - ret = salt_cli.run( - "pip.install", - ["pep8"], - log=str(log_file), - bin_env=venv_dir, - minion_tgt=salt_minion.id, - ) - - if not isinstance(ret.data, dict): - pytest.fail( - "The 'pip.install' command did not return the expected dictionary." - " Output:\n{}".format(ret) - ) - - if _check_download_error(ret.stdout): - pytest.skip("Test skipped due to pip download error") - assert ret.returncode == 0 - assert log_file.exists() - assert "pep8" in log_file.read_text() - - -@pytest.mark.skipif( - shutil.which("/bin/pip3") is None, reason="Could not find /bin/pip3" -) -@pytest.mark.skip_on_windows(reason="test specific for linux usage of /bin/python") -@pytest.mark.skip_initial_gh_actions_failure( - reason="This was skipped on older golden images and is failing on newer." -) -def test_system_pip3(salt_cli, salt_minion): - salt_cli.run( - "pip.install", - pkgs=["lazyimport==0.0.1"], - bin_env="/bin/pip3", - minion_tgt=salt_minion.id, - ) - ret1 = salt_cli.run( - "cmd.run_all", "/bin/pip3 freeze | grep lazyimport", minion_tgt=salt_minion.id - ) - assert "lazyimport==0.0.1" in ret1.stdout - - salt_cli.run( - "pip.uninstall", - pkgs=["lazyimport"], - bin_env="/bin/pip3", - minion_tgt=salt_minion.id, - ) - ret2 = salt_cli.run( - "cmd.run_all", "/bin/pip3 freeze | grep lazyimport", minion_tgt=salt_minion.id - ) - assert ret2.data["stdout"] == ""