1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
import subprocess
from pathlib import Path
import pytest
from cyclopts._edit import (
EditorDidNotChangeError,
EditorDidNotSaveError,
EditorError,
EditorNotFoundError,
edit,
)
@pytest.fixture
def mock_editor():
"""Mock editor that simulates saving file with edited content."""
def fake_editor(args):
Path(args[1]).write_text("edited content")
return 0
return fake_editor
def test_basic_edit(mocker, mock_editor, monkeypatch):
"""Test basic editing functionality."""
monkeypatch.setenv("EDITOR", "test_editor")
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=mock_editor)
result = edit("initial text")
assert result == "edited content"
def test_custom_path(mocker, mock_editor, tmp_path, monkeypatch):
"""Test editing with custom path."""
custom_path = tmp_path / "custom.txt"
monkeypatch.setenv("EDITOR", "test_editor")
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=mock_editor)
result = edit("initial text", path=custom_path)
assert result == "edited content"
def test_editor_not_found(mocker, monkeypatch):
"""Test behavior when no editor is found."""
monkeypatch.delenv("EDITOR", raising=False)
mocker.patch("shutil.which", return_value=False)
with pytest.raises(EditorNotFoundError):
edit("test")
def test_did_not_save(mocker, monkeypatch):
"""Test behavior when user doesn't save."""
monkeypatch.setenv("EDITOR", "test_editor")
def fake_editor(_): # Doesn't "save"
return 0
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=fake_editor)
with pytest.raises(EditorDidNotSaveError):
edit("initial text", save=True)
def test_did_not_change(mocker, monkeypatch):
"""Test behavior when content isn't changed."""
monkeypatch.setenv("EDITOR", "test_editor")
initial_text = "unchanged text"
def fake_editor(args):
assert args[0] == "test_editor"
Path(args[1]).write_text(initial_text)
return 0
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=fake_editor)
with pytest.raises(EditorDidNotChangeError):
edit(initial_text, required=True)
def test_editor_error(mocker, monkeypatch):
"""Test handling of editor errors."""
monkeypatch.setenv("EDITOR", "test_editor")
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=subprocess.CalledProcessError(1, "test_editor"))
with pytest.raises(EditorError) as exc_info:
edit("test")
assert "status 1" in str(exc_info.value)
def test_custom_encoding(mocker, tmp_path, monkeypatch):
"""Test custom encoding support."""
test_path = tmp_path / "test.txt"
monkeypatch.setenv("EDITOR", "test_editor")
def fake_editor_with_encoding(args):
assert args[0] == "test_editor"
Path(args[1]).write_text("текст", encoding="utf-16")
return 0
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=fake_editor_with_encoding)
result = edit("initial", path=test_path, encoding="utf-16")
assert result == "текст"
def test_fallback_editors(mocker, mock_editor, monkeypatch):
"""Test fallback editor selection."""
monkeypatch.delenv("EDITOR", raising=False)
def mock_which(editor_name):
return editor_name == "vim"
mocker.patch("shutil.which", side_effect=mock_which)
mock_check_call = mocker.patch("subprocess.check_call", side_effect=mock_editor)
result = edit("test", fallback_editors=["emacs", "vim", "nano"])
assert result == "edited content"
assert mock_check_call.call_args_list[0].args[0][0] == "vim"
def test_editor_args(mocker, monkeypatch):
"""Test passing additional arguments to editor."""
monkeypatch.setenv("EDITOR", "test_editor")
def check_editor_args(args):
assert args[0] == "test_editor"
assert "--no-splash" in args
Path(args[1]).write_text("edited with args")
return 0
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=check_editor_args)
result = edit("test", editor_args=["--no-splash"])
assert result == "edited with args"
def test_optional_change(mocker, monkeypatch):
"""Test when content changes are optional."""
monkeypatch.setenv("EDITOR", "test_editor")
initial_text = "unchanged"
def fake_editor(args):
assert args[0] == "test_editor"
Path(args[1]).write_text(initial_text)
return 0
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=fake_editor)
# Should not raise DidNotChangeError
result = edit(initial_text, required=False)
assert result == initial_text
def test_file_cleanup(mocker, tmp_path, monkeypatch, mock_editor):
"""Test temporary file cleanup."""
test_path = tmp_path / "cleanup_test.txt"
monkeypatch.setenv("EDITOR", "test_editor")
mocker.patch("shutil.which", return_value=True)
mocker.patch("subprocess.check_call", side_effect=mock_editor)
edit("test", path=test_path)
assert not test_path.exists()
|