This commit is contained in:
2026-06-22 12:31:26 +08:00
parent 413ab40044
commit 0df795237d
9 changed files with 474 additions and 105 deletions
+73 -39
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from unittest.mock import MagicMock, patch
@@ -17,51 +18,84 @@ from pyflowx.cli import sshcopyid
class TestSshCopyId:
"""Test ssh_copy_id function."""
def test_ssh_copy_id_success(self) -> None:
"""ssh_copy_id should deploy SSH key successfully."""
pytest.importorskip("paramiko")
with patch("paramiko.SSHClient") as mock_ssh_client, patch.object(
Path, "exists", return_value=True
), patch.object(Path, "read_text", return_value="ssh-rsa AAAAB3..."):
mock_client = MagicMock()
mock_ssh_client.return_value = mock_client
mock_client.connect.return_value = None
mock_client.exec_command.return_value = (MagicMock(), MagicMock(), MagicMock())
def test_ssh_copy_id_pub_key_not_exists(self, tmp_path: Path) -> None:
"""Should handle nonexistent public key."""
with patch.object(Path, "expanduser", return_value=tmp_path / "nonexistent.pub"), pytest.raises(SystemExit):
sshcopyid.ssh_copy_id("localhost", "user", "password")
result = sshcopyid.ssh_copy_id("localhost", "user", "password")
assert result is None # Function doesn't return anything
def test_ssh_copy_id_sshpass_not_found(self, tmp_path: Path) -> None:
"""Should handle sshpass not found."""
pub_key = tmp_path / "id_rsa.pub"
pub_key.write_text("ssh-rsa AAAAB3...")
def test_ssh_copy_id_with_custom_port(self) -> None:
"""ssh_copy_id should handle custom port."""
pytest.importorskip("paramiko")
with patch("paramiko.SSHClient") as mock_ssh_client, patch.object(
Path, "exists", return_value=True
), patch.object(Path, "read_text", return_value="ssh-rsa AAAAB3..."):
mock_client = MagicMock()
mock_ssh_client.return_value = mock_client
mock_client.connect.return_value = None
mock_client.exec_command.return_value = (MagicMock(), MagicMock(), MagicMock())
with patch.object(Path, "expanduser", return_value=pub_key), patch(
"subprocess.run", side_effect=FileNotFoundError
), pytest.raises(SystemExit):
sshcopyid.ssh_copy_id("localhost", "user", "password")
def test_ssh_copy_id_timeout(self, tmp_path: Path) -> None:
"""Should handle SSH timeout."""
pub_key = tmp_path / "id_rsa.pub"
pub_key.write_text("ssh-rsa AAAAB3...")
with patch.object(Path, "expanduser", return_value=pub_key), patch(
"subprocess.run", side_effect=subprocess.TimeoutExpired("cmd", 30)
), pytest.raises(SystemExit):
sshcopyid.ssh_copy_id("localhost", "user", "password")
def test_ssh_copy_id_process_error(self, tmp_path: Path) -> None:
"""Should handle SSH process error."""
pub_key = tmp_path / "id_rsa.pub"
pub_key.write_text("ssh-rsa AAAAB3...")
with patch.object(Path, "expanduser", return_value=pub_key), patch(
"subprocess.run", side_effect=subprocess.CalledProcessError(1, "cmd")
), pytest.raises(SystemExit):
sshcopyid.ssh_copy_id("localhost", "user", "password")
def test_ssh_copy_id_success(self, tmp_path: Path) -> None:
"""Should deploy SSH key successfully."""
pub_key = tmp_path / "id_rsa.pub"
pub_key.write_text("ssh-rsa AAAAB3...")
with patch.object(Path, "expanduser", return_value=pub_key), patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
sshcopyid.ssh_copy_id("localhost", "user", "password")
assert mock_run.called
def test_ssh_copy_id_with_custom_port(self, tmp_path: Path) -> None:
"""Should handle custom port."""
pub_key = tmp_path / "id_rsa.pub"
pub_key.write_text("ssh-rsa AAAAB3...")
with patch.object(Path, "expanduser", return_value=pub_key), patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
sshcopyid.ssh_copy_id("localhost", "user", "password", port=2222)
# Verify that connect was called with custom port
mock_client.connect.assert_called_once()
call_args = mock_client.connect.call_args
assert call_args[1]["port"] == 2222
# Verify port is used
call_args = mock_run.call_args[0][0]
assert "2222" in call_args
def test_ssh_copy_id_with_custom_keypath(self) -> None:
"""ssh_copy_id should handle custom key path."""
pytest.importorskip("paramiko")
with patch("paramiko.SSHClient") as mock_ssh_client, patch.object(
Path, "exists", return_value=True
), patch.object(Path, "read_text", return_value="ssh-rsa AAAAB3..."):
mock_client = MagicMock()
mock_ssh_client.return_value = mock_client
mock_client.connect.return_value = None
mock_client.exec_command.return_value = (MagicMock(), MagicMock(), MagicMock())
def test_ssh_copy_id_with_custom_keypath(self, tmp_path: Path) -> None:
"""Should handle custom keypath."""
custom_key = tmp_path / "custom.pub"
custom_key.write_text("ssh-rsa AAAAB3...")
result = sshcopyid.ssh_copy_id("localhost", "user", "password", keypath="/custom/key.pub")
# Verify that the custom keypath was used
assert result is None
with patch.object(Path, "expanduser", return_value=custom_key), patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
sshcopyid.ssh_copy_id("localhost", "user", "password", keypath=str(custom_key))
assert mock_run.called
def test_ssh_copy_id_with_custom_timeout(self, tmp_path: Path) -> None:
"""Should handle custom timeout."""
pub_key = tmp_path / "id_rsa.pub"
pub_key.write_text("ssh-rsa AAAAB3...")
with patch.object(Path, "expanduser", return_value=pub_key), patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
sshcopyid.ssh_copy_id("localhost", "user", "password", timeout=60)
# Verify timeout is used in ConnectTimeout option
call_args = mock_run.call_args[0][0]
assert "ConnectTimeout=60" in call_args
# ---------------------------------------------------------------------- #