feat: complete CLI, Docker & deployment docs (Step 10.3)
- Add comprehensive CLI tests for seed and serve commands - Create README.md with development setup, deployment guide, and environment variable reference - Mark Step 10.3 as complete in PLAN.md This completes the final implementation step. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
241
tests/test_cli_seed.py
Normal file
241
tests/test_cli_seed.py
Normal file
@@ -0,0 +1,241 @@
|
||||
# ABOUTME: End-to-end tests for seed CLI command.
|
||||
# ABOUTME: Tests seeding reference data via subprocess.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from animaltrack.db import get_db
|
||||
from animaltrack.repositories import (
|
||||
FeedTypeRepository,
|
||||
LocationRepository,
|
||||
ProductRepository,
|
||||
SpeciesRepository,
|
||||
UserRepository,
|
||||
)
|
||||
|
||||
# Get the project root directory (parent of tests/)
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
|
||||
|
||||
class TestSeedCLI:
|
||||
"""End-to-end tests for seed command."""
|
||||
|
||||
def test_seed_command_success(self, tmp_path):
|
||||
"""Should seed data via CLI and exit 0."""
|
||||
db_path = tmp_path / "test.db"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["DB_PATH"] = str(db_path)
|
||||
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
||||
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "animaltrack.cli", "seed"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
||||
assert "seed data loaded" in result.stdout.lower()
|
||||
|
||||
def test_seed_creates_users(self, tmp_path):
|
||||
"""Should create expected users."""
|
||||
db_path = tmp_path / "test.db"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["DB_PATH"] = str(db_path)
|
||||
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
||||
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "animaltrack.cli", "seed"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
||||
|
||||
# Verify users were created
|
||||
db = get_db(str(db_path))
|
||||
repo = UserRepository(db)
|
||||
users = repo.list_all()
|
||||
|
||||
usernames = {u.username for u in users}
|
||||
assert "ppetru" in usernames
|
||||
assert "ines" in usernames
|
||||
assert "guest" in usernames
|
||||
|
||||
def test_seed_creates_locations(self, tmp_path):
|
||||
"""Should create expected locations."""
|
||||
db_path = tmp_path / "test.db"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["DB_PATH"] = str(db_path)
|
||||
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
||||
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "animaltrack.cli", "seed"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
||||
|
||||
# Verify locations were created
|
||||
db = get_db(str(db_path))
|
||||
repo = LocationRepository(db)
|
||||
locations = repo.list_all()
|
||||
|
||||
names = {loc.name for loc in locations}
|
||||
assert "Strip 1" in names
|
||||
assert "Strip 4" in names
|
||||
assert "Nursery 1" in names
|
||||
assert "Nursery 4" in names
|
||||
assert len(locations) == 8
|
||||
|
||||
def test_seed_creates_species(self, tmp_path):
|
||||
"""Should create expected species."""
|
||||
db_path = tmp_path / "test.db"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["DB_PATH"] = str(db_path)
|
||||
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
||||
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "animaltrack.cli", "seed"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
||||
|
||||
# Verify species were created
|
||||
db = get_db(str(db_path))
|
||||
repo = SpeciesRepository(db)
|
||||
species_list = repo.list_all()
|
||||
|
||||
codes = {s.code for s in species_list}
|
||||
assert "duck" in codes
|
||||
assert "goose" in codes
|
||||
assert "sheep" in codes
|
||||
|
||||
def test_seed_creates_products(self, tmp_path):
|
||||
"""Should create expected products."""
|
||||
db_path = tmp_path / "test.db"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["DB_PATH"] = str(db_path)
|
||||
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
||||
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "animaltrack.cli", "seed"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
||||
|
||||
# Verify products were created
|
||||
db = get_db(str(db_path))
|
||||
repo = ProductRepository(db)
|
||||
products = repo.list_all()
|
||||
|
||||
codes = {p.code for p in products}
|
||||
assert "egg.duck" in codes
|
||||
assert "meat" in codes
|
||||
assert "offal" in codes
|
||||
assert "fat" in codes
|
||||
assert "bones" in codes
|
||||
assert "feathers" in codes
|
||||
assert "down" in codes
|
||||
|
||||
def test_seed_creates_feed_types(self, tmp_path):
|
||||
"""Should create expected feed types."""
|
||||
db_path = tmp_path / "test.db"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["DB_PATH"] = str(db_path)
|
||||
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
||||
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "animaltrack.cli", "seed"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"stdout: {result.stdout}, stderr: {result.stderr}"
|
||||
|
||||
# Verify feed types were created
|
||||
db = get_db(str(db_path))
|
||||
repo = FeedTypeRepository(db)
|
||||
feed_types = repo.list_all()
|
||||
|
||||
codes = {f.code for f in feed_types}
|
||||
assert "starter" in codes
|
||||
assert "grower" in codes
|
||||
assert "layer" in codes
|
||||
|
||||
def test_seed_is_idempotent(self, tmp_path):
|
||||
"""Running seed twice should produce same result."""
|
||||
db_path = tmp_path / "test.db"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["DB_PATH"] = str(db_path)
|
||||
env["CSRF_SECRET"] = "test-secret-for-csrf"
|
||||
env["PYTHONPATH"] = str(PROJECT_ROOT / "src")
|
||||
|
||||
# Run seed first time
|
||||
result1 = subprocess.run(
|
||||
[sys.executable, "-m", "animaltrack.cli", "seed"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
assert result1.returncode == 0, f"stdout: {result1.stdout}, stderr: {result1.stderr}"
|
||||
|
||||
# Run seed second time
|
||||
result2 = subprocess.run(
|
||||
[sys.executable, "-m", "animaltrack.cli", "seed"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
cwd=str(PROJECT_ROOT),
|
||||
)
|
||||
assert result2.returncode == 0, f"stdout: {result2.stdout}, stderr: {result2.stderr}"
|
||||
|
||||
# Verify counts haven't doubled
|
||||
db = get_db(str(db_path))
|
||||
|
||||
user_repo = UserRepository(db)
|
||||
assert len(user_repo.list_all()) == 3
|
||||
|
||||
location_repo = LocationRepository(db)
|
||||
assert len(location_repo.list_all()) == 8
|
||||
|
||||
species_repo = SpeciesRepository(db)
|
||||
assert len(species_repo.list_all()) == 3
|
||||
|
||||
product_repo = ProductRepository(db)
|
||||
assert len(product_repo.list_all()) == 7
|
||||
|
||||
feed_repo = FeedTypeRepository(db)
|
||||
assert len(feed_repo.list_all()) == 3
|
||||
Reference in New Issue
Block a user