CLI implementation

This commit is contained in:
2026-02-04 01:10:58 -05:00
parent 33687865fd
commit 77cc907f6e
8 changed files with 1853 additions and 1 deletions

187
tests/unit/test_cli.py Normal file
View File

@@ -0,0 +1,187 @@
"""
Tests for CLI module.
"""
import pytest
import tempfile
from pathlib import Path
from unittest.mock import patch, MagicMock
from typer.testing import CliRunner
from src.cli.main import app
runner = CliRunner()
# Valid storyboard JSON template
VALID_STORYBOARD = """
{
"schema_version": "1.0",
"project": {
"title": "Test Storyboard",
"fps": 24,
"target_duration_s": 20,
"resolution": {"width": 1280, "height": 720},
"aspect_ratio": "16:9"
},
"shots": [
{
"id": "shot_001",
"duration_s": 4,
"prompt": "A test shot description",
"camera": {"framing": "wide", "movement": "static"},
"generation": {"seed": 42, "steps": 30, "cfg_scale": 6.0}
}
],
"output": {
"container": "mp4",
"codec": "h264",
"crf": 18,
"preset": "medium"
}
}
"""
class TestValidateCommand:
"""Test the validate command."""
def test_validate_nonexistent_file(self):
"""Test validation with non-existent file."""
result = runner.invoke(app, ["validate", "nonexistent.json"])
assert result.exit_code == 1
assert "not found" in result.output
def test_validate_valid_storyboard(self):
"""Test validation with valid storyboard."""
with tempfile.TemporaryDirectory() as tmpdir:
storyboard_file = Path(tmpdir) / "test.json"
storyboard_file.write_text(VALID_STORYBOARD)
result = runner.invoke(app, ["validate", str(storyboard_file)])
assert result.exit_code == 0
assert "valid" in result.output
assert "Test Storyboard" in result.output
def test_validate_verbose(self):
"""Test validation with verbose flag."""
with tempfile.TemporaryDirectory() as tmpdir:
storyboard_file = Path(tmpdir) / "test.json"
storyboard_file.write_text(VALID_STORYBOARD)
result = runner.invoke(app, ["validate", str(storyboard_file), "--verbose"])
assert result.exit_code == 0
assert "Shots" in result.output
class TestListBackendsCommand:
"""Test the list-backends command."""
def test_list_backends(self):
"""Test listing available backends."""
result = runner.invoke(app, ["list-backends"])
assert result.exit_code == 0
assert "wan" in result.output
assert "Available Backends" in result.output
class TestGenerateCommand:
"""Test the generate command."""
@pytest.fixture
def mock_storyboard(self):
"""Create a mock storyboard file."""
with tempfile.TemporaryDirectory() as tmpdir:
storyboard_file = Path(tmpdir) / "test.json"
storyboard_file.write_text(VALID_STORYBOARD)
yield storyboard_file
def test_generate_dry_run(self, mock_storyboard):
"""Test generate command with dry-run flag."""
result = runner.invoke(app, [
"generate",
str(mock_storyboard),
"--dry-run"
])
assert result.exit_code == 0
assert "Dry run complete" in result.output
def test_generate_nonexistent_storyboard(self):
"""Test generate with non-existent storyboard."""
result = runner.invoke(app, ["generate", "nonexistent.json"])
assert result.exit_code == 1
assert "not found" in result.output
class TestResumeCommand:
"""Test the resume command."""
def test_resume_nonexistent_project(self):
"""Test resume with non-existent project."""
with tempfile.TemporaryDirectory() as tmpdir:
result = runner.invoke(app, [
"resume",
"nonexistent_project",
"--output", tmpdir
])
assert result.exit_code == 1
assert "No checkpoint found" in result.output
def test_resume_existing_project(self):
"""Test resume with existing project checkpoint."""
with tempfile.TemporaryDirectory() as tmpdir:
project_dir = Path(tmpdir) / "test_project"
project_dir.mkdir()
checkpoint_db = project_dir / "checkpoints.db"
checkpoint_db.touch()
# Create a mock checkpoint
with patch('src.cli.main.CheckpointManager') as mock_mgr:
mock_checkpoint = MagicMock()
mock_checkpoint.storyboard_path = str(Path(tmpdir) / "storyboard.json")
mock_mgr.return_value.get_project_checkpoint.return_value = mock_checkpoint
# Create storyboard file
storyboard_file = Path(tmpdir) / "storyboard.json"
storyboard_file.write_text(VALID_STORYBOARD)
result = runner.invoke(app, [
"resume",
"test_project",
"--output", tmpdir
])
# Should attempt to resume (may fail on actual generation)
assert "Resuming project" in result.output or result.exit_code != 0
class TestCLIHelp:
"""Test CLI help messages."""
def test_main_help(self):
"""Test main help message."""
result = runner.invoke(app, ["--help"])
assert result.exit_code == 0
assert "Storyboard to Video Generation Pipeline" in result.output
def test_generate_help(self):
"""Test generate command help."""
result = runner.invoke(app, ["generate", "--help"])
assert result.exit_code == 0
assert "Generate video from storyboard" in result.output
assert "--output" in result.output
assert "--backend" in result.output
assert "--dry-run" in result.output
def test_validate_help(self):
"""Test validate command help."""
result = runner.invoke(app, ["validate", "--help"])
assert result.exit_code == 0
assert "Validate a storyboard file" in result.output
def test_resume_help(self):
"""Test resume command help."""
result = runner.invoke(app, ["resume", "--help"])
assert result.exit_code == 0
assert "Resume a failed or interrupted project" in result.output