diff --git a/tests/test_external_editor_integration.py b/tests/test_external_editor_integration.py new file mode 100644 index 0000000..bef780d --- /dev/null +++ b/tests/test_external_editor_integration.py @@ -0,0 +1,114 @@ +"""Integration tests for external editor - launches real editors.""" +import os +import tempfile +import pytest +from unittest.mock import patch, MagicMock +from src.external_editor import ( + ExternalEditorLauncher, + ExternalEditorConfig, + TextEditorConfig, + create_temp_modified_file, +) + + +@pytest.fixture +def vscode_editor(): + return TextEditorConfig(name="vscode", path="code.exe", diff_args=["--diff"]) + + +@pytest.fixture +def ext_config(vscode_editor): + return ExternalEditorConfig(editors={"vscode": vscode_editor}, default_editor="vscode") + + +@pytest.fixture +def launcher(ext_config): + return ExternalEditorLauncher(ext_config) + + +class TestExternalEditorIntegration: + def test_create_temp_modified_file_creates_valid_file(self): + content = "line1\nline2\nline3\n" + path = create_temp_modified_file(content) + try: + assert os.path.exists(path) + with open(path, encoding="utf-8") as f: + assert f.read() == content + finally: + if os.path.exists(path): + os.unlink(path) + + def test_build_diff_command_format(self, launcher, vscode_editor): + cmd = launcher.build_diff_command(vscode_editor, "original.txt", "modified.txt") + assert cmd == ["code.exe", "--diff", "original.txt", "modified.txt"] + + @patch("subprocess.Popen") + def test_launch_diff_calls_subprocess(self, mock_popen, launcher): + mock_popen.return_value = MagicMock() + result = launcher.launch_diff("vscode", "orig.txt", "mod.txt") + assert result is not None + mock_popen.assert_called_once_with(["code.exe", "--diff", "orig.txt", "mod.txt"]) + + @patch("subprocess.Popen") + def test_launch_diff_with_real_files(self, mock_popen, launcher): + mock_popen.return_value = MagicMock() + with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False, encoding="utf-8") as f: + f.write("original content\n") + orig_path = f.name + try: + mod_path = create_temp_modified_file("modified content\n") + try: + result = launcher.launch_diff("vscode", orig_path, mod_path) + assert result is not None + call_args = mock_popen.call_args[0][0] + assert call_args[0] == "code.exe" + assert call_args[1] == "--diff" + assert call_args[2] == orig_path + assert call_args[3] == mod_path + finally: + if os.path.exists(mod_path): + os.unlink(mod_path) + finally: + if os.path.exists(orig_path): + os.unlink(orig_path) + + +class TestExternalEditorWithRealVSCode: + @pytest.mark.skipif( + os.environ.get("TEST_REAL_VSCODE") != "1", + reason="Set TEST_REAL_VSCODE=1 environment variable to run this test" + ) + def test_launch_real_vscode_diff(self): + vscode_path = os.environ.get("VSCODE_PATH", "code.exe") + if not os.path.exists(vscode_path) and vscode_path == "code.exe": + import shutil + if not shutil.which("code"): + pytest.skip("VSCode not found in PATH") + config = ExternalEditorConfig( + editors={"vscode": TextEditorConfig(name="vscode", path=vscode_path, diff_args=["--diff"])}, + default_editor="vscode" + ) + launcher = ExternalEditorLauncher(config) + with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False, encoding="utf-8") as f: + f.write("line1\nline2\nline3\n") + orig_path = f.name + try: + mod_path = create_temp_modified_file("line1\nmodified line2\nline3\n") + try: + import subprocess + proc = launcher.launch_diff("vscode", orig_path, mod_path) + assert proc is not None + import time + time.sleep(1) + proc.terminate() + proc.wait(timeout=5) + finally: + if os.path.exists(mod_path): + os.unlink(mod_path) + finally: + if os.path.exists(orig_path): + os.unlink(orig_path) + + +if __name__ == "__main__": + pytest.main([__file__, "-v"])