import os
from pathlib import Path
from subprocess import CompletedProcess
from unittest.mock import MagicMock, patch
import pytest
from isort import exceptions, hooks
from isort._version import _IS_COMPILED
def test_git_hook(src_dir: str) -> None:
"""Simple smoke level testing of git hooks"""
with patch(
"subprocess.run", return_value=CompletedProcess("command", returncode=0, stdout=b"")
) as run_mock:
hooks.git_hook()
run_mock.assert_called_once()
assert run_mock.call_args[0][0] == [
"git",
"diff-index",
"--cached",
"--name-only",
"--diff-filter=ACMRTUXB",
"HEAD",
]
with patch(
"subprocess.run", return_value=CompletedProcess("command", returncode=0, stdout=b"")
) as run_mock:
hooks.git_hook(lazy=True)
run_mock.assert_called_once()
assert run_mock.call_args[0][0] == [
"git",
"diff-index",
"--name-only",
"--diff-filter=ACMRTUXB",
"HEAD",
]
with patch(
"subprocess.run",
return_value=CompletedProcess(
"command", returncode=0, stdout=b"README.md\nsetup.cfg\nLICDENSE\nmkdocs.yml\ntest"
),
) as run_mock:
hooks.git_hook(modify=True)
run_mock.assert_called_once()
file_name = os.path.join(src_dir, "main.py")
with patch(
"subprocess.run",
side_effect=[
CompletedProcess("command", returncode=0, stdout=file_name.encode()),
CompletedProcess("command", returncode=0, stdout=b"import b,a"),
],
) as run_mock:
errors = hooks.git_hook(modify=True, strict=True)
assert run_mock.call_count == 2
assert errors == 1
@pytest.mark.skipif(reason="Can't use these mocks in mypyc-compiled code.", condition=_IS_COMPILED)
def test_git_hook_with_mocks(src_dir: str) -> None:
"""
Additional tests for the git hook, split off to allow running some when compiled.
"""
mock_main_py = MagicMock(return_value=[os.path.join(src_dir, "main.py")])
mock_imperfect = MagicMock()
mock_imperfect.return_value.stdout = b"import b\nimport a"
with patch("isort.hooks.get_lines", mock_main_py):
with patch("subprocess.run", mock_imperfect):
with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock:
hooks.git_hook(modify=False)
api_mock.assert_not_called()
with patch("isort.hooks.get_lines", MagicMock(return_value=[os.path.join(src_dir, "main.py")])):
class FakeProcessResponse:
stdout = b"# isort: skip-file\nimport b\nimport a\n"
with patch("subprocess.run", MagicMock(return_value=FakeProcessResponse())):
with patch("isort.api", MagicMock(side_effect=exceptions.FileSkipped("", ""))):
hooks.git_hook(modify=True)
def test_git_hook_lazy(tmp_path: Path) -> None:
has_problems = tmp_path / "test_has_problems.py"
has_problems.write_text("import b\nimport a\n", "utf8")
with patch(
"subprocess.run",
side_effect=[
CompletedProcess("command", returncode=0, stdout=str(has_problems).encode()),
RuntimeError("donotcall"),
],
) as run_mock:
found_errors = hooks.git_hook(lazy=True, strict=True)
assert run_mock.call_count == 1
assert found_errors == 1
@pytest.mark.skipif(reason="Can't use these mocks in mypyc-compiled code.", condition=_IS_COMPILED)
def test_git_hook_uses_the_configuration_file_specified_in_settings_path(tmp_path: Path) -> None:
subdirectory_path = tmp_path / "subdirectory"
configuration_file_path = subdirectory_path / ".isort.cfg"
modified_file_path = configuration_file_path.parent.parent / "somefile.py"
section = "testsection"
os.mkdir(subdirectory_path)
with open(configuration_file_path, "w") as fd:
fd.write("[isort]\n")
fd.write(f"sections={section}")
with open(modified_file_path, "w") as fd:
pass
files_modified = [str(modified_file_path.absolute())]
with patch("isort.hooks.get_lines", MagicMock(return_value=files_modified)):
with patch("isort.hooks.get_output", MagicMock(return_value="")):
with patch("isort.api.check_code_string", MagicMock()) as run_mock:
hooks.git_hook(settings_file=str(configuration_file_path))
assert run_mock.call_args[1]["config"].sections == (section,)