diff --git a/upgrade/scripts/utils.py b/upgrade/scripts/utils.py index a55d947..7f7c733 100644 --- a/upgrade/scripts/utils.py +++ b/upgrade/scripts/utils.py @@ -1,4 +1,6 @@ import logging +import os +import stat from pathlib import Path from sys import platform @@ -22,3 +24,19 @@ def platform_specific_python_path(venv_path: str) -> str: def is_windows() -> bool: return platform == "win32" or platform == "cygwin" + + +def on_rm_error(_func, path, _exc_info): + """Used by when calling rmtree to ensure that readonly files and folders + are deleted. + """ + try: + os.chmod(path, stat.S_IWRITE) + except OSError as e: + logger.debug(f"File at path {path} not found, error trace - {e}") + return + try: + os.unlink(path) + except (OSError, PermissionError) as e: + logger.debug(f"WARNING: Failed to clean up files: {str(e)}.") + pass diff --git a/upgrade/tests/manage_venv/conftest.py b/upgrade/tests/manage_venv/conftest.py index 1aefe27..e196c5e 100644 --- a/upgrade/tests/manage_venv/conftest.py +++ b/upgrade/tests/manage_venv/conftest.py @@ -2,13 +2,37 @@ from pathlib import Path import pytest +from mock import patch from upgrade.tests.utils import remove_directory +from upgrade.scripts.upgrade_python_package import run + +from upgrade.scripts.manage_venv import _get_venv_executable + +from ..conftest import REPOSITORY_WHEELS_PATH, original_executable THIS_FOLDER = Path(__file__).parent REPOSITORY_DIR = THIS_FOLDER.parent / "repository" +def _create_venv(path, version): + venv_name = f"oll-test-top-level~={version}" + dependency_to_install = f"oll-test-top-level=={version}" + + venv_path = str(Path(path, venv_name)) + run(original_executable, "-m", "venv", venv_path) + venv_executable = _get_venv_executable(venv_path) + run( + venv_executable, + "-m", + "pip", + "install", + dependency_to_install, + "--find-links", + str(REPOSITORY_WHEELS_PATH), + ) + + def env_fixture(make_dir=True): def decorator(func): @pytest.fixture(scope="module", autouse=True) @@ -38,6 +62,24 @@ def envs_home(request, path=""): yield path +@pytest.fixture() +def mock_cloudsmith_url_valid(): + with patch("upgrade.scripts.validations.is_cloudsmith_url_valid", lambda *_,: True): + yield + + +@env_fixture() +def initial_v2_0_0_venv(request, path=""): + _create_venv(path, "2.0.0") + yield + + +@env_fixture() +def initial_v2_0_1_venv(request, path=""): + _create_venv(path, "2.0.1") + yield + + @pytest.fixture(scope="module") def top_level_requirements(request, envs_home): test_package = Path(envs_home) / "test_repository" diff --git a/upgrade/tests/manage_venv/test_build_and_upgrade_existing_venv.py b/upgrade/tests/manage_venv/test_build_and_upgrade_existing_venv.py new file mode 100644 index 0000000..866ed76 --- /dev/null +++ b/upgrade/tests/manage_venv/test_build_and_upgrade_existing_venv.py @@ -0,0 +1,126 @@ +from mock import patch +from pathlib import Path + +from upgrade.scripts.manage_venv import ( + build_and_upgrade_venv, + venv_pip, + _get_venv_executable, +) + + +def test_build_and_upgrade_venv_where_v2_0_0_venv_exists_and_auto_upgrade_is_enabled_expect_venv_upgraded_to_v2_0_1( + initial_v2_0_0_venv, envs_home, wheels_dir, mock_cloudsmith_url_valid +): + dependency_to_install = "oll-test-top-level~=2.0.0" + expected_installed_version = "2.0.1" + + cut = build_and_upgrade_venv + + with patch( + "upgrade.scripts.manage_venv.determine_compatible_upgrade_version", + lambda *_,: expected_installed_version, + ): + cut( + dependency_to_install, + envs_home, + auto_upgrade=True, + cloudsmith_url="", + wheels_path=str(wheels_dir), + update_from_local_wheels=True, + ) + + venv_path = Path(envs_home, dependency_to_install) + venv_executable = _get_venv_executable(venv_path) + + venv_pip(venv_executable, "check") + + dependencies_from_venv = venv_pip( + venv_executable, + "list", + "--format=freeze", + "--exclude-editable", + ).splitlines() + + expected_packages = { + f"oll-test-top-level=={expected_installed_version}", + f"oll-dependency1=={expected_installed_version}", + f"oll-dependency2=={expected_installed_version}", + } + actual_packages = set(dependencies_from_venv) + + expected = True + actual = expected_packages.issubset(actual_packages) + assert actual == expected + + +def test_build_and_upgrade_venv_where_v2_0_1_venv_exists_and_auto_upgrade_is_disabled_expect_venv_unchanged( + initial_v2_0_1_venv, envs_home, wheels_dir, mock_cloudsmith_url_valid, capfd +): + dependency_to_install = "oll-test-top-level~=2.0.1" + expected_installed_version = "2.0.1" + + cut = build_and_upgrade_venv + + with patch( + "upgrade.scripts.manage_venv.determine_compatible_upgrade_version", + lambda *_,: expected_installed_version, + ): + cut( + dependency_to_install, + envs_home, + auto_upgrade=False, + cloudsmith_url="", + wheels_path=str(wheels_dir), + update_from_local_wheels=True, + ) + out, _ = capfd.readouterr() # TODO: read from logs since capture does not work from venv + + expected = "Requirements did not change. Returning venv executable." + actual = out + + assert expected in actual + + +def test_build_and_upgrade_venv_where_v2_0_1_venv_exists_and_auto_upgrade_is_enabled_expect_venv_unchanged_and_on_same_version( + initial_v2_0_1_venv, envs_home, wheels_dir, mock_cloudsmith_url_valid, capfd +): + dependency_to_install = "oll-test-top-level~=2.0.1" + expected_installed_version = "2.0.1" + + cut = build_and_upgrade_venv + + with patch( + "upgrade.scripts.manage_venv.determine_compatible_upgrade_version", + lambda *_,: expected_installed_version, + ): + cut( + dependency_to_install, + envs_home, + auto_upgrade=True, + cloudsmith_url="", + wheels_path=str(wheels_dir), + update_from_local_wheels=True, + ) + + venv_path = Path(envs_home, dependency_to_install) + venv_executable = _get_venv_executable(venv_path) + + venv_pip(venv_executable, "check") + + dependencies_from_venv = venv_pip( + venv_executable, + "list", + "--format=freeze", + "--exclude-editable", + ).splitlines() + + expected_packages = { + f"oll-test-top-level=={expected_installed_version}", + f"oll-dependency1=={expected_installed_version}", + f"oll-dependency2=={expected_installed_version}", + } + actual_packages = set(dependencies_from_venv) + + expected = True + actual = expected_packages.issubset(actual_packages) + assert actual == expected