- Add migrations.py with create_migration, run_migrations, get_db_version - Implement CLI migrate and create-migration commands - Use FastMigrate's sequential 0001-*.sql naming convention - Add comprehensive unit, integration, and E2E tests (35 tests) - Add fresh_db_path and temp_migrations_dir test fixtures 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
181 lines
6.3 KiB
Python
181 lines
6.3 KiB
Python
# ABOUTME: End-to-end tests for migration CLI commands.
|
|
# ABOUTME: Tests migrate and create-migration commands via subprocess.
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Get the project root directory (parent of tests/)
|
|
PROJECT_ROOT = Path(__file__).parent.parent
|
|
|
|
|
|
class TestMigrateCLI:
|
|
"""End-to-end tests for migrate command."""
|
|
|
|
def test_migrate_command_success(self, tmp_path, temp_migrations_dir):
|
|
"""Should run migrations via CLI and exit 0."""
|
|
# Use a path that doesn't exist yet
|
|
db_path = tmp_path / "test.db"
|
|
|
|
# Create a migration file
|
|
migration_file = temp_migrations_dir / "0001-test.sql"
|
|
migration_file.write_text("CREATE TABLE test (id INTEGER PRIMARY KEY);")
|
|
|
|
# Set required env vars with PYTHONPATH to find the package
|
|
# Note: Settings uses no env_prefix, so use DB_PATH not AT_DB_PATH
|
|
env = os.environ.copy()
|
|
env["DB_PATH"] = str(db_path)
|
|
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
|
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
|
|
|
# Run migrate command
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "animaltrack.cli", "migrate"],
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
cwd=str(temp_migrations_dir.parent),
|
|
)
|
|
|
|
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
|
assert "success" in result.stdout.lower() or "completed" in result.stdout.lower()
|
|
|
|
def test_migrate_command_no_migrations(self, tmp_path, temp_migrations_dir):
|
|
"""Should succeed when no migrations to run."""
|
|
# Use a path that doesn't exist yet
|
|
db_path = tmp_path / "test.db"
|
|
|
|
# Set required env vars
|
|
env = os.environ.copy()
|
|
env["DB_PATH"] = str(db_path)
|
|
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
|
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
|
|
|
# Run migrate command with empty migrations dir
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "animaltrack.cli", "migrate"],
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
cwd=str(temp_migrations_dir.parent),
|
|
)
|
|
|
|
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
|
|
|
def test_migrate_command_failure(self, tmp_path, temp_migrations_dir):
|
|
"""Should exit 1 on migration failure."""
|
|
# Use a path that doesn't exist yet
|
|
db_path = tmp_path / "test.db"
|
|
|
|
# Create a broken migration file
|
|
migration_file = temp_migrations_dir / "0001-broken.sql"
|
|
migration_file.write_text("THIS IS INVALID SQL;")
|
|
|
|
# Set required env vars
|
|
env = os.environ.copy()
|
|
env["DB_PATH"] = str(db_path)
|
|
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
|
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
|
|
|
# Run migrate command
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "animaltrack.cli", "migrate"],
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
cwd=str(temp_migrations_dir.parent),
|
|
)
|
|
|
|
assert result.returncode == 1
|
|
|
|
|
|
class TestCreateMigrationCLI:
|
|
"""End-to-end tests for create-migration command."""
|
|
|
|
def test_create_migration_command(self, temp_migrations_dir):
|
|
"""Should create migration file via CLI."""
|
|
# Set required env vars
|
|
env = os.environ.copy()
|
|
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
|
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
|
|
|
# Run create-migration command
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "animaltrack.cli", "create-migration", "add users table"],
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
cwd=str(temp_migrations_dir.parent),
|
|
)
|
|
|
|
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
|
|
|
# Verify file was created
|
|
created_files = list(temp_migrations_dir.glob("0001-*.sql"))
|
|
assert len(created_files) == 1
|
|
assert "add-users-table" in created_files[0].name
|
|
|
|
def test_create_migration_shows_path(self, temp_migrations_dir):
|
|
"""Should print path to created migration."""
|
|
# Set required env vars
|
|
env = os.environ.copy()
|
|
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
|
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
|
|
|
# Run create-migration command
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "animaltrack.cli", "create-migration", "test migration"],
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
cwd=str(temp_migrations_dir.parent),
|
|
)
|
|
|
|
assert result.returncode == 0
|
|
assert "0001-test-migration.sql" in result.stdout
|
|
|
|
def test_create_migration_increments_index(self, temp_migrations_dir):
|
|
"""Should create migrations with incrementing indexes."""
|
|
# Set required env vars
|
|
env = os.environ.copy()
|
|
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
|
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
|
|
|
# Create first migration
|
|
subprocess.run(
|
|
[sys.executable, "-m", "animaltrack.cli", "create-migration", "first"],
|
|
capture_output=True,
|
|
env=env,
|
|
cwd=str(temp_migrations_dir.parent),
|
|
)
|
|
|
|
# Create second migration
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "animaltrack.cli", "create-migration", "second"],
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
cwd=str(temp_migrations_dir.parent),
|
|
)
|
|
|
|
assert result.returncode == 0
|
|
assert "0002-second.sql" in result.stdout
|
|
|
|
def test_create_migration_requires_description(self, temp_migrations_dir):
|
|
"""Should fail when description not provided."""
|
|
env = os.environ.copy()
|
|
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
|
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
|
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "animaltrack.cli", "create-migration"],
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
cwd=str(temp_migrations_dir.parent),
|
|
)
|
|
|
|
# argparse should reject missing required argument
|
|
assert result.returncode != 0
|