diff --git a/tests/conftest.py b/tests/conftest.py index ce05107..8336dc8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -104,6 +104,8 @@ def __post_init__(self) -> None: interactive=False, enable_color=False, ) + self.application.data_dir = self.isolated_data_dir + self.application.project = self.project self.default_environment = self.reload_environment("default") self.test_environment = self.reload_environment("test") diff --git a/tests/test_integration.py b/tests/test_integration.py index b0317a6..0956212 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -8,12 +8,18 @@ from typing import Dict, Type +import hatch import packaging.requirements import pytest from hatch_pip_compile.installer import PluginInstaller from tests.conftest import PipCompileFixture +try: + match_major, hatch_minor, _ = hatch._version.__version__.split(".") +except AttributeError: + match_major, hatch_minor, _ = hatch.__about__.__version__.split(".") + @pytest.mark.parametrize("installer", ["pip", "pip-sync"]) def test_new_dependency( @@ -34,6 +40,11 @@ def test_new_dependency( assert updated_environment.lockfile_up_to_date is True new_lockfile_requirements = pip_compile.default_environment.piptools_lock.read_requirements() assert new_lockfile_requirements == [packaging.requirements.Requirement("requests")] + result = updated_environment.plugin_check_command( + command=["python", "-m", "pip", "list"], + capture_output=True, + ) + assert "requests" in result.stdout.decode() @pytest.mark.parametrize("installer", ["pip", "pip-sync"]) @@ -69,3 +80,68 @@ def test_create_constraint_environment(pip_compile: PipCompileFixture) -> None: pip_compile.application.prepare_environment(environment=test_environment) new_lockfile_requirements = pip_compile.default_environment.piptools_lock.read_requirements() assert new_lockfile_requirements == [packaging.requirements.Requirement("requests")] + result = test_environment.plugin_check_command( + command=["python", "-m", "pip", "list"], + capture_output=True, + ) + assert "pytest" in result.stdout.decode() + + +def test_dependency_uninstalled(pip_compile: PipCompileFixture) -> None: + """ + An environment is prepared, then a dependency is uninstalled, + the environment should be out of sync even though the lockfile + is good + """ + pip_compile.application.prepare_environment(environment=pip_compile.test_environment) + list_result = pip_compile.test_environment.plugin_check_command( + command=["python", "-m", "pip", "list"], + capture_output=True, + ) + assert "pytest" in list_result.stdout.decode() + assert pip_compile.test_environment.dependencies_in_sync() is True + pip_compile.test_environment.plugin_check_command( + command=["python", "-m", "pip", "uninstall", "pytest", "pytest-cov", "-y"], + ) + new_list_result = pip_compile.test_environment.plugin_check_command( + command=["python", "-m", "pip", "list"], + capture_output=True, + ) + assert "pytest" not in new_list_result.stdout.decode() + assert pip_compile.test_environment.lockfile_up_to_date is True + assert pip_compile.test_environment.dependencies_in_sync() is False + + +def test_lockfile_missing(pip_compile: PipCompileFixture) -> None: + """ + Lockfile missing on previously prepared environment + """ + # Prepare the test environment, assert it is in sync + pip_compile.application.prepare_environment(environment=pip_compile.test_environment) + assert pip_compile.test_environment.dependencies_in_sync() is True + # Delete the lockfile, assert environment is in sync but lockfile is missing + pip_compile.test_environment.piptools_lock_file.unlink() + updated_environment = pip_compile.reload_environment("test") + list_result = updated_environment.plugin_check_command( + command=["python", "-m", "pip", "list"], + capture_output=True, + ) + assert "pytest" in list_result.stdout.decode() + assert updated_environment.dependencies_in_sync() is False + # Prepare the environment again, assert it is in sync + pip_compile.application.prepare_environment(environment=updated_environment) + new_updated_environment = pip_compile.reload_environment("test") + assert new_updated_environment.dependencies_in_sync() is True + assert new_updated_environment.piptools_lock_file.exists() is True + + +@pytest.mark.skipif(match_major == "1" and hatch_minor == "7", reason="hatch 1.8.0+ required") +def test_check_dependency_hash_creates_lock(pip_compile: PipCompileFixture) -> None: + """ + Calling `dependency_hash` creates a lockfile when one does not exist + """ + pip_compile.application.prepare_environment(environment=pip_compile.default_environment) + pip_compile.default_environment.piptools_lock_file.unlink() + updated_environment = pip_compile.reload_environment("default") + _ = updated_environment.dependency_hash() + assert updated_environment.piptools_lock_file.exists() is True diff --git a/tests/test_integration_cli.py b/tests/test_integration_cli.py new file mode 100644 index 0000000..9537491 --- /dev/null +++ b/tests/test_integration_cli.py @@ -0,0 +1,93 @@ +""" +Integration Tests using the CLI +""" + +import hatch.cli +import hatch.cli.env +from click.testing import CliRunner + +from tests.conftest import PipCompileFixture + + +def test_invoke_environment_creates_env(pip_compile: PipCompileFixture) -> None: + """ + Test using the CLI runner + """ + runner = CliRunner() + environment = pip_compile.test_environment + venv = environment.virtual_env.directory + assert not venv.exists() + with runner.isolated_filesystem(pip_compile.isolation): + result = runner.invoke( + hatch.cli.hatch, + args=["env", "run", "--env", environment.name, "--", "python", "--version"], + ) + assert result.exit_code == 0 + assert venv.exists() + + +def test_missing_lockfile_created(pip_compile: PipCompileFixture) -> None: + """ + Running the CLI without a lockfile creates one + """ + runner = CliRunner() + environment = pip_compile.default_environment + venv = environment.virtual_env.directory + environment.piptools_lock_file.unlink() + assert not environment.piptools_lock_file.exists() + with runner.isolated_filesystem(pip_compile.isolation): + result = runner.invoke( + hatch.cli.hatch, + args=["env", "run", "--env", environment.name, "--", "python", "--version"], + ) + assert result.exit_code == 0 + assert environment.piptools_lock_file.exists() + assert venv.exists() + + +def test_constraint_env_created(pip_compile: PipCompileFixture) -> None: + """ + Running the CLI with a constraint env creates one + """ + runner = CliRunner() + environment = pip_compile.test_environment + environment.piptools_lock_file.unlink() + environment.constraint_env.piptools_lock_file.unlink() + with runner.isolated_filesystem(pip_compile.isolation): + result = runner.invoke( + hatch.cli.hatch, + args=["env", "run", "--env", environment.name, "--", "python", "--version"], + ) + assert result.exit_code == 0 + assert environment.piptools_lock_file.exists() + assert environment.constraint_env.piptools_lock_file.exists() + assert environment.virtual_env.directory.exists() + assert environment.constraint_env.virtual_env.directory.exists() + + +def test_missing_lockfile_after_prepared(pip_compile: PipCompileFixture) -> None: + """ + After an environment is prepared the lockfile is deleted and recreated the next time + """ + runner = CliRunner() + environment = pip_compile.default_environment + # Create the environment the first time + with runner.isolated_filesystem(pip_compile.isolation): + result = runner.invoke( + hatch.cli.hatch, + args=["env", "run", "--env", environment.name, "--", "python", "--version"], + ) + assert result.exit_code == 0 + # Delete the lockfile + assert environment.piptools_lock_file.exists() + environment.piptools_lock_file.unlink() + assert not environment.piptools_lock_file.exists() + # Run the environment again + with runner.isolated_filesystem(pip_compile.isolation): + result = runner.invoke( + hatch.cli.hatch, + args=["env", "run", "--env", environment.name, "--", "python", "--version"], + ) + assert result.exit_code == 0 + # Assert the lockfile was recreated + assert environment.piptools_lock_file.exists()