Files
dl-organizer/tests/test_cli_rollback.py
T

197 lines
6.3 KiB
Python
Raw Normal View History

2026-02-09 17:43:35 +08:00
"""Tests for CLI rollback command."""
import json
from pathlib import Path
from datetime import datetime, timezone
2026-02-09 17:43:35 +08:00
import pytest
from click.testing import CliRunner
from vlm.cli import main
from vlm.models import FileOperation, OperationResult, RollbackLog
@pytest.fixture
def cli_runner():
"""Create a Click CLI test runner."""
return CliRunner()
@pytest.fixture
def sample_rollback_log(tmp_path):
"""Create a sample rollback log file for testing."""
# Create test files
source1 = tmp_path / "source1.txt"
source2 = tmp_path / "source2.txt"
dest1 = tmp_path / "dest1.txt"
dest2 = tmp_path / "dest2.txt"
source1.write_text("content1")
source2.write_text("content2")
# Move files to simulate execution
source1.rename(dest1)
source2.rename(dest2)
# Create rollback log
operations = [
OperationResult(
operation=FileOperation(
operation_type="move",
source_path=source1,
destination_path=dest1,
reason="test move 1",
has_conflict=False,
conflict_reason=None
),
success=True,
error_message=None,
executed_at=datetime.now(timezone.utc)
2026-02-09 17:43:35 +08:00
),
OperationResult(
operation=FileOperation(
operation_type="move",
source_path=source2,
destination_path=dest2,
reason="test move 2",
has_conflict=False,
conflict_reason=None
),
success=True,
error_message=None,
executed_at=datetime.now(timezone.utc)
2026-02-09 17:43:35 +08:00
)
]
rollback_log = RollbackLog(
log_id="test-log-id",
execution_plan_id="test-plan-id",
executed_at=datetime.now(timezone.utc),
2026-02-09 17:43:35 +08:00
operations=operations
)
# Save rollback log to file
log_path = tmp_path / "rollback_test.json"
log_data = {
"log_id": rollback_log.log_id,
"execution_plan_id": rollback_log.execution_plan_id,
"executed_at": rollback_log.executed_at.isoformat(),
"operations": [
{
"operation_type": op.operation.operation_type,
"source_path": str(op.operation.source_path),
"destination_path": str(op.operation.destination_path),
"reason": op.operation.reason,
"success": op.success,
"error_message": op.error_message,
"executed_at": op.executed_at.isoformat()
}
for op in rollback_log.operations
]
}
with open(log_path, 'w', encoding='utf-8') as f:
json.dump(log_data, f, indent=2)
return {
"log_path": log_path,
"source1": source1,
"source2": source2,
"dest1": dest1,
"dest2": dest2
}
class TestRollbackCommand:
"""Tests for the rollback CLI command."""
def test_rollback_help(self, cli_runner):
"""Test that rollback command shows help text."""
result = cli_runner.invoke(main, ['rollback', '--help'])
assert result.exit_code == 0
assert "Rollback previous execution" in result.output
assert "--log" in result.output
assert "best-effort" in result.output
def test_rollback_with_log_file(self, cli_runner, sample_rollback_log):
"""Test rollback command with explicit log file."""
log_path = sample_rollback_log["log_path"]
dest1 = sample_rollback_log["dest1"]
dest2 = sample_rollback_log["dest2"]
source1 = sample_rollback_log["source1"]
source2 = sample_rollback_log["source2"]
# Verify files are at destination before rollback
assert dest1.exists()
assert dest2.exists()
assert not source1.exists()
assert not source2.exists()
# Run rollback command with auto-confirmation
result = cli_runner.invoke(
main,
['rollback', '--log', str(log_path)],
input='y\n' # Confirm rollback
)
# Check command succeeded
assert result.exit_code == 0
assert "Rollback log loaded" in result.output
assert "Rolling back" in result.output
assert "Rollback Summary" in result.output
# Verify files were moved back to source
assert source1.exists()
assert source2.exists()
assert not dest1.exists()
assert not dest2.exists()
def test_rollback_cancel_confirmation(self, cli_runner, sample_rollback_log):
"""Test that rollback can be cancelled at confirmation prompt."""
log_path = sample_rollback_log["log_path"]
dest1 = sample_rollback_log["dest1"]
dest2 = sample_rollback_log["dest2"]
# Run rollback command and cancel
result = cli_runner.invoke(
main,
['rollback', '--log', str(log_path)],
input='n\n' # Cancel rollback
)
# Check command was cancelled
assert result.exit_code == 0
assert "Rollback cancelled" in result.output
# Verify files were NOT moved (still at destination)
assert dest1.exists()
assert dest2.exists()
def test_rollback_missing_log_file(self, cli_runner, tmp_path):
"""Test rollback command with missing log file."""
missing_log = tmp_path / "nonexistent.json"
result = cli_runner.invoke(
main,
['rollback', '--log', str(missing_log)]
)
# Check command failed with appropriate error
# Click returns exit code 2 for file validation errors
assert result.exit_code == 2
assert "Error" in result.output or "does not exist" in result.output
def test_rollback_no_log_specified_no_logs_exist(self, cli_runner, tmp_path, monkeypatch):
"""Test rollback command without log file when no logs exist."""
# Mock home directory to use tmp_path
fake_home = tmp_path / "fake_home"
fake_home.mkdir()
monkeypatch.setattr(Path, 'home', lambda: fake_home)
result = cli_runner.invoke(main, ['rollback'])
# Check command failed with appropriate error
assert result.exit_code == 1
assert "No rollback logs found" in result.output