Skip to content

Commit 4e24cb4

Browse files
committed
✅ Add CLI unit tests
Introduce comprehensive test suite for CLI functionality This commit adds a new test_cli.py file with extensive unit tests for the AeonSync CLI. Key additions include: - Mock fixtures for config manager, AeonBackup, AeonRestore, and ListBackups - Tests for sync command with various options - Tests for restore command, including interactive mode - Tests for list-backups command - Tests for config command, covering show, set, add/remove source directories, and multiple changes These tests ensure robust CLI functionality and improve overall code quality and reliability.
1 parent 72b653d commit 4e24cb4

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

tests/test_cli.py

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# pylint: disable=redefined-outer-name
2+
"""Test cases for the AeonSync CLI functionality."""
3+
4+
from unittest.mock import patch
5+
6+
import pytest
7+
from typer.testing import CliRunner
8+
9+
from aeonsync.cli import app
10+
from aeonsync.config import BackupConfig
11+
12+
runner = CliRunner()
13+
14+
15+
@pytest.fixture
16+
def mock_config_manager():
17+
"""Fixture for mocking the config manager."""
18+
with patch("aeonsync.cli.config_manager") as mock:
19+
yield mock
20+
21+
22+
@pytest.fixture
23+
def mock_aeon_backup():
24+
"""Fixture for mocking AeonBackup."""
25+
with patch("aeonsync.cli.AeonBackup") as mock:
26+
yield mock
27+
28+
29+
@pytest.fixture
30+
def mock_aeon_restore():
31+
"""Fixture for mocking AeonRestore."""
32+
with patch("aeonsync.cli.AeonRestore") as mock:
33+
yield mock
34+
35+
36+
@pytest.fixture
37+
def mock_list_backups():
38+
"""Fixture for mocking ListBackups."""
39+
with patch("aeonsync.cli.ListBackups") as mock:
40+
yield mock
41+
42+
43+
def test_sync_command(mock_aeon_backup):
44+
"""Test the basic sync command without options."""
45+
result = runner.invoke(app, ["sync"])
46+
assert result.exit_code == 0
47+
mock_aeon_backup.assert_called_once()
48+
mock_aeon_backup.return_value.create_backup.assert_called_once()
49+
50+
51+
@patch("aeonsync.cli.Path.exists")
52+
@patch("aeonsync.cli.Path.is_dir")
53+
def test_sync_command_with_options(mock_is_dir, mock_exists, mock_aeon_backup):
54+
"""Test the sync command with various options."""
55+
mock_exists.return_value = True
56+
mock_is_dir.return_value = True
57+
result = runner.invoke(
58+
app, ["sync", "--source", "/test/path", "--retention", "30", "--dry-run"]
59+
)
60+
assert result.exit_code == 0, f"Command failed with output: {result.output}"
61+
mock_aeon_backup.assert_called_once()
62+
args, _ = mock_aeon_backup.call_args
63+
64+
assert isinstance(args[0], BackupConfig)
65+
config = args[0]
66+
67+
assert "/test/path" in config.sources
68+
assert config.retention_period == 30
69+
assert config.dry_run is True
70+
71+
72+
def test_restore_command(mock_aeon_restore):
73+
"""Test the restore command with specific file and date."""
74+
result = runner.invoke(app, ["restore", "/test/file.txt", "2023-01-01"])
75+
assert result.exit_code == 0
76+
mock_aeon_restore.assert_called_once()
77+
mock_aeon_restore.return_value.restore_file_versions.assert_called_once_with(
78+
"/test/file.txt", "2023-01-01", None, diff=False, preview=False
79+
)
80+
81+
82+
def test_restore_command_interactive(mock_aeon_restore):
83+
"""Test the interactive restore command."""
84+
result = runner.invoke(app, ["restore", "--interactive"])
85+
assert result.exit_code == 0
86+
mock_aeon_restore.assert_called_once()
87+
mock_aeon_restore.return_value.restore_interactive.assert_called_once_with(
88+
diff=False, preview=False
89+
)
90+
91+
92+
def test_list_backups_command(mock_list_backups):
93+
"""Test the list-backups command."""
94+
result = runner.invoke(app, ["list-backups"])
95+
assert result.exit_code == 0
96+
mock_list_backups.assert_called_once()
97+
mock_list_backups.return_value.list.assert_called_once()
98+
99+
100+
def test_config_command_show(mock_config_manager):
101+
"""Test the config command with --show option."""
102+
mock_config_manager.config = {"test_key": "test_value"}
103+
result = runner.invoke(app, ["config", "--show"])
104+
assert result.exit_code == 0
105+
assert "test_key" in result.output
106+
assert "test_value" in result.output
107+
108+
109+
def test_config_command_set(mock_config_manager):
110+
"""Test setting a configuration value."""
111+
result = runner.invoke(app, ["config", "--hostname", "new_host"])
112+
assert result.exit_code == 0
113+
mock_config_manager.set.assert_called_once_with("hostname", "new_host")
114+
115+
116+
def test_config_command_add_source_dir(mock_config_manager):
117+
"""Test adding a source directory to the configuration."""
118+
result = runner.invoke(app, ["config", "--add-source-dir", "/new/source"])
119+
assert result.exit_code == 0
120+
mock_config_manager.add_to_list.assert_called_once_with(
121+
"source_dirs", "/new/source"
122+
)
123+
124+
125+
def test_config_command_remove_source_dir(mock_config_manager):
126+
"""Test removing a source directory from the configuration."""
127+
result = runner.invoke(app, ["config", "--remove-source-dir", "/old/source"])
128+
assert result.exit_code == 0
129+
mock_config_manager.remove_from_list.assert_called_once_with(
130+
"source_dirs", "/old/source"
131+
)
132+
133+
134+
def test_config_command_multiple_changes(mock_config_manager):
135+
"""Test setting multiple configuration values at once."""
136+
result = runner.invoke(
137+
app,
138+
[
139+
"config",
140+
"--hostname",
141+
"new_host",
142+
"--remote-address",
143+
"new_remote",
144+
"--retention-period",
145+
"14",
146+
],
147+
)
148+
assert result.exit_code == 0
149+
mock_config_manager.set.assert_any_call("hostname", "new_host")
150+
mock_config_manager.set.assert_any_call("remote_address", "new_remote")
151+
mock_config_manager.set.assert_any_call("retention_period", 14)
152+
153+
154+
# Add more tests as needed for other CLI functionalities

0 commit comments

Comments
 (0)