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
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()
),
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()
)
]
rollback_log = RollbackLog(
log_id="test-log-id",
execution_plan_id="test-plan-id",
executed_at=datetime.now(),
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