Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 255 additions & 16 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,250 @@

import os

from typing import TYPE_CHECKING

import pytest

from poetry.core.constraints.version import Version

from tests.helpers import MOCK_DEFAULT_GIT_REVISION
from tests.helpers import MockDulwichRepo
from tests.helpers import copy_path
from tests.helpers import flatten_dict
from tests.helpers import get_dependency
from tests.helpers import get_package
from tests.helpers import isolated_environment


if TYPE_CHECKING:
from pathlib import Path

from poetry.core.packages.dependency import Dependency
from poetry.core.packages.package import Package


class TestGetPackage:
@pytest.fixture
def base_package(self) -> Package:
return get_package("test-package", "1.0.0")

def test_get_package_with_string_name(self, base_package: Package) -> None:
assert base_package.name == "test-package"

@pytest.mark.parametrize(
"version_input,expected_version",
[("1.0.0", "1.0.0"), (Version.parse("2.0.0"), "2.0.0")],
)
def test_get_package_version_handling(
self, version_input: str | Version, expected_version: str
) -> None:
package = get_package("test-package", version_input)
assert str(package.version) == expected_version

@pytest.mark.parametrize(
"yanked_input,expected_yanked,expected_reason",
[
(False, False, None),
(True, True, None),
("security vulnerability", True, "security vulnerability"),
],
)
def test_get_package_yanked_handling(
self,
yanked_input: bool | str,
expected_yanked: bool,
expected_reason: str | None,
) -> None:
package = get_package("test-package", "1.0.0", yanked=yanked_input)
assert package.yanked is expected_yanked
if isinstance(yanked_input, str):
assert package.yanked_reason == expected_reason


class TestGetDependency:
@pytest.fixture
def base_dependency(self) -> Dependency:
return get_dependency("test-package")

def test_get_dependency_name(self, base_dependency: Dependency) -> None:
assert base_dependency.name == "test-package"

def test_get_dependency_default_constraint_value(
self, base_dependency: Dependency
) -> None:
assert str(base_dependency.constraint) == "*"

def test_get_dependency_defaults_to_false_optional(
self, base_dependency: Dependency
) -> None:
assert base_dependency.is_optional() is False

def test_get_dependency_default_to_false_allow_prereleases(
self, base_dependency: Dependency
) -> None:
assert base_dependency.allows_prereleases() is False

@pytest.mark.parametrize(
"constraint,expected_constraint",
[(None, "*"), ("^1.0.0", "^1.0.0"), ({"version": "^2.0.0"}, "^2.0.0")],
)
def test_get_dependency_constraint_handling(
self, constraint: None | str | dict[str, str], expected_constraint: str
) -> None:
dependency = get_dependency("test-package", constraint)
assert dependency.pretty_constraint == expected_constraint

def test_get_dependency_with_complex_dict_constraint(self) -> None:
constraint = {"version": "^2.0.0", "extras": ["feature1"]}
dependency = get_dependency("test-package", constraint)
assert dependency.pretty_constraint == "^2.0.0"
assert "feature1" in dependency.extras

def test_get_dependency_with_groups(self) -> None:
dependency = get_dependency("test-package", groups=["dev", "test"])
assert dependency.groups == frozenset(["dev", "test"])

@pytest.mark.parametrize(
"optional,allows_prereleases",
[(True, True), (True, False), (False, True), (False, False)],
)
def test_get_dependency_boolean_params(
self, optional: bool, allows_prereleases: bool
) -> None:
dependency = get_dependency(
"test-package", optional=optional, allows_prereleases=allows_prereleases
)
assert dependency.is_optional() is optional
assert dependency.allows_prereleases() is allows_prereleases


class TestCopyPath:
@pytest.fixture
def setup_files(self, tmp_path: Path) -> dict[str, Path]:
# Create source file
source_file = tmp_path / "source_file.txt"
source_file.write_text("source content")

# Create source directory with content
source_dir = tmp_path / "source_dir"
source_dir.mkdir()
(source_dir / "file1.txt").write_text("file1 content")
(source_dir / "file2.txt").write_text("file2 content")

# Create existing destination file
dest_file = tmp_path / "dest_file.txt"
dest_file.write_text("destination content")

# Create existing destination directory with content
dest_dir = tmp_path / "dest_dir"
dest_dir.mkdir()
(dest_dir / "existing.txt").write_text("existing content")

return {
"source_file": source_file,
"source_dir": source_dir,
"dest_file": dest_file,
"dest_dir": dest_dir,
"tmp_path": tmp_path,
}

def test_copy_file_to_new_destination(self, setup_files: dict[str, Path]) -> None:
source = setup_files["source_file"]
dest = setup_files["tmp_path"] / "new_file.txt"

copy_path(source, dest)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Suggest adding tests for edge cases in copy_path

It would be beneficial to add tests for cases such as copying a file to a destination where the parent directory doesn't exist, copying an empty directory, copying a directory to a file when the file is a symlink, and handling potential exceptions during the copy process (e.g., permission errors).

Suggested implementation:

    def test_copy_file_to_existing_file(self, setup_files: dict[str, Path]) -> None:
        source = setup_files["source_file"]
        dest = setup_files["dest_file"]

        copy_path(source, dest)

        assert dest.exists()
        assert dest.read_text() == "source content"

    def test_copy_file_to_destination_missing_parent(self, setup_files: dict[str, Path], tmp_path: Path) -> None:
        source = setup_files["source_file"]
        dest = tmp_path / "non_existent_dir" / "copied_file.txt"
        # Ensure the parent directory does not exist
        if dest.parent.exists():
            import shutil
            shutil.rmtree(dest.parent)
        copy_path(source, dest)
        assert dest.exists()
        assert dest.read_text() == "source content"

    def test_copy_empty_directory(self, tmp_path: Path) -> None:
        # Create an empty source directory
        source_dir = tmp_path / "empty_src"
        source_dir.mkdir()
        dest_dir = tmp_path / "copied_empty_dir"
        copy_path(source_dir, dest_dir)
        assert dest_dir.exists()
        # Ensure the destination directory is empty
        assert len(list(dest_dir.iterdir())) == 0

    def test_copy_directory_to_symlink(self, tmp_path: Path) -> None:
        # Create a source directory with a file
        source_dir = tmp_path / "src_dir"
        source_dir.mkdir()
        (source_dir / "file.txt").write_text("123")
        # Create a target file and a symlink for the destination
        target_file = tmp_path / "target.txt"
        target_file.write_text("old content")
        dest_symlink = tmp_path / "symlink_file"
        dest_symlink.symlink_to(target_file)
        import pytest
        # Expecting an exception because copying a directory to a symlinked file is invalid
        with pytest.raises(Exception):
            copy_path(source_dir, dest_symlink)

    def test_copy_path_permission_error(self, setup_files: dict[str, Path], tmp_path: Path) -> None:
        source = setup_files["source_file"]
        dest = tmp_path / "restricted_dir" / "file.txt"
        dest.parent.mkdir()
        import os
        # Change permissions to simulate a permission error (read-only directory)
        os.chmod(dest.parent, 0o400)
        import pytest
        with pytest.raises(PermissionError):
            copy_path(source, dest)
        # Revert permissions for cleanup
        os.chmod(dest.parent, 0o700)

These changes assume that your copy_path function is designed to raise appropriate exceptions when errors occur, and that it can handle directory copying. You might need to import modules like shutil, os, or pytest at the top of the file if they are not already imported.


assert dest.exists()
assert dest.read_text() == "source content"

def test_copy_file_to_existing_file(self, setup_files: dict[str, Path]) -> None:
source = setup_files["source_file"]
dest = setup_files["dest_file"]

copy_path(source, dest)

assert dest.exists()
assert dest.read_text() == "source content"

def test_copy_file_to_existing_directory(
self, setup_files: dict[str, Path]
) -> None:
source = setup_files["source_file"]
dest = setup_files["dest_dir"]

copy_path(source, dest)

assert dest.exists()
assert dest.is_file() # Directory is replaced with file
assert dest.read_text() == "source content"

def test_copy_directory_to_new_destination(
self, setup_files: dict[str, Path]
) -> None:
source = setup_files["source_dir"]
dest = setup_files["tmp_path"] / "new_dir"

copy_path(source, dest)

assert dest.is_dir()
assert (dest / "file1.txt").read_text() == "file1 content"
assert (dest / "file2.txt").read_text() == "file2 content"

def test_copy_directory_to_existing_directory(
self, setup_files: dict[str, Path]
) -> None:
source = setup_files["source_dir"]
dest = setup_files["dest_dir"]

copy_path(source, dest)

assert dest.is_dir()
assert (dest / "file1.txt").read_text() == "file1 content"
assert (dest / "file2.txt").read_text() == "file2 content"
assert not (dest / "existing.txt").exists() # Old content is removed

def test_copy_directory_to_existing_file(
self, setup_files: dict[str, Path]
) -> None:
source = setup_files["source_dir"]
dest = setup_files["dest_file"]

copy_path(source, dest)

assert dest.is_dir()
assert (dest / "file1.txt").read_text() == "file1 content"
assert (dest / "file2.txt").read_text() == "file2 content"


class TestMockDulwichRepo:
@pytest.fixture
def repo_path(self, tmp_path: Path) -> Path:
return tmp_path / "mock_repo"

def test_init_with_path_object(self, repo_path: Path) -> None:
repo_path.mkdir()
repo = MockDulwichRepo(repo_path)
assert repo.path == str(repo_path)

def test_init_with_string_path(self, repo_path: Path) -> None:
repo_path.mkdir()
repo = MockDulwichRepo(str(repo_path))
assert repo.path == str(repo_path)

def test_head_returns_default_revision_encoded(self, repo_path: Path) -> None:
repo_path.mkdir()
repo = MockDulwichRepo(repo_path)
expected = MOCK_DEFAULT_GIT_REVISION.encode()
assert repo.head() == expected
assert isinstance(repo.head(), bytes)

def test_init_ignores_extra_kwargs(self, repo_path: Path) -> None:
repo_path.mkdir()
repo = MockDulwichRepo(repo_path, ignored_param="value", another_param=123)
assert repo.path == str(repo_path)
assert repo.head() == MOCK_DEFAULT_GIT_REVISION.encode()


def test_flatten_dict() -> None:
orig_dict = {
"a": 1,
Expand All @@ -26,21 +266,20 @@ def test_flatten_dict() -> None:
assert flattened_dict == flatten_dict(orig_dict, delimiter=":")


def test_isolated_environment_restores_original_environ() -> None:
original_environ = dict(os.environ)
with isolated_environment():
os.environ["TEST_VAR"] = "test"
assert os.environ == original_environ


def test_isolated_environment_clears_environ() -> None:
os.environ["TEST_VAR"] = "test"
with isolated_environment(clear=True):
assert "TEST_VAR" not in os.environ
assert "TEST_VAR" in os.environ
class TestIsolatedEnvironment:
def test_isolated_environment_restores_original_environ(self) -> None:
original_environ = dict(os.environ)
with isolated_environment():
os.environ["TEST_VAR"] = "test"
assert os.environ == original_environ

def test_isolated_environment_clears_environ(self) -> None:
os.environ["TEST_VAR"] = "test"
with isolated_environment(clear=True):
assert "TEST_VAR" not in os.environ
assert "TEST_VAR" in os.environ

def test_isolated_environment_updates_environ() -> None:
with isolated_environment(environ={"NEW_VAR": "new_value"}):
assert os.environ["NEW_VAR"] == "new_value"
assert "NEW_VAR" not in os.environ
def test_isolated_environment_updates_environ(self) -> None:
with isolated_environment(environ={"NEW_VAR": "new_value"}):
assert os.environ["NEW_VAR"] == "new_value"
assert "NEW_VAR" not in os.environ
Loading