promptlooper/backend/tests/test_stack_integration.py
John Lightner 7dad9d97af MAESTRO: Add entrypoint migrations, worker config, and stack integration tests
Create docker/entrypoint.sh to run alembic migrations on API startup.
Create backend/worker.py with Celery app config for the compose worker service.
Fix README single-container port (8000) and add production compose documentation.
Add 27 tests (stack integration + worker) verifying all Docker/compose artifacts
are present, consistent, and the /health endpoint responds correctly.
2026-04-07 02:09:56 -05:00

138 lines
4.4 KiB
Python

"""Stack integration verification tests.
These tests verify that all configuration files needed for 'docker compose up'
are present, consistent, and well-formed. They do NOT start actual containers.
"""
import os
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2] # repo root
class TestDockerComposeConfig:
"""Verify docker-compose.yml references are satisfied."""
def test_docker_compose_exists(self):
assert (ROOT / "docker-compose.yml").is_file()
def test_dockerfile_exists(self):
assert (ROOT / "docker" / "Dockerfile").is_file()
def test_nginx_conf_exists(self):
assert (ROOT / "docker" / "nginx.conf").is_file()
def test_entrypoint_exists(self):
assert (ROOT / "docker" / "entrypoint.sh").is_file()
def test_requirements_txt_exists(self):
assert (ROOT / "backend" / "requirements.txt").is_file()
def test_alembic_ini_exists(self):
assert (ROOT / "alembic.ini").is_file()
def test_alembic_env_exists(self):
assert (ROOT / "alembic" / "env.py").is_file()
def test_alembic_has_migration(self):
versions = list((ROOT / "alembic" / "versions").glob("*.py"))
assert len(versions) >= 1, "Expected at least one Alembic migration"
class TestDockerfileConsistency:
"""Verify Dockerfile references match actual files."""
def test_dockerfile_copies_backend(self):
content = (ROOT / "docker" / "Dockerfile").read_text()
assert "COPY backend/" in content
def test_dockerfile_copies_alembic(self):
content = (ROOT / "docker" / "Dockerfile").read_text()
assert "COPY alembic/" in content
assert "COPY alembic.ini" in content
def test_dockerfile_copies_entrypoint(self):
content = (ROOT / "docker" / "Dockerfile").read_text()
assert "entrypoint.sh" in content
def test_dockerfile_runs_migrations_via_entrypoint(self):
content = (ROOT / "docker" / "entrypoint.sh").read_text()
assert "alembic upgrade head" in content
class TestNginxConfig:
"""Verify nginx proxies correctly."""
def test_nginx_proxies_api(self):
content = (ROOT / "docker" / "nginx.conf").read_text()
assert "proxy_pass http://promptlooper-api:8000" in content
def test_nginx_proxies_websocket(self):
content = (ROOT / "docker" / "nginx.conf").read_text()
assert "upgrade" in content.lower()
def test_nginx_serves_spa_fallback(self):
content = (ROOT / "docker" / "nginx.conf").read_text()
assert "try_files" in content
assert "/index.html" in content
class TestFrontendBuildability:
"""Verify frontend has all files needed for a build."""
def test_package_json_exists(self):
assert (ROOT / "frontend" / "package.json").is_file()
def test_index_html_exists(self):
assert (ROOT / "frontend" / "index.html").is_file()
def test_main_tsx_exists(self):
assert (ROOT / "frontend" / "src" / "main.tsx").is_file()
def test_app_tsx_exists(self):
assert (ROOT / "frontend" / "src" / "App.tsx").is_file()
def test_all_page_components_exist(self):
pages = [
"SetupPage", "LoginPage", "DashboardPage", "ProjectsPage",
"ExperimentPage", "LivePage", "ComparePage", "AdminPage",
]
for page in pages:
assert (ROOT / "frontend" / "src" / "pages" / f"{page}.tsx").is_file(), f"Missing {page}.tsx"
def test_vite_config_exists(self):
assert (ROOT / "frontend" / "vite.config.ts").is_file()
def test_tailwind_config_exists(self):
assert (ROOT / "frontend" / "tailwind.config.js").is_file()
class TestWorkerConfig:
"""Verify Celery worker module exists and is importable."""
def test_worker_module_exists(self):
assert (ROOT / "backend" / "worker.py").is_file()
class TestHealthEndpoint:
"""Verify /health endpoint works in test mode."""
def test_health_returns_ok(self):
from fastapi.testclient import TestClient
# Ensure backend is importable
import sys
backend_dir = str(ROOT / "backend")
if backend_dir not in sys.path:
sys.path.insert(0, backend_dir)
from main import app
client = TestClient(app)
resp = client.get("/health")
assert resp.status_code == 200
data = resp.json()
assert data["status"] in ("ok", "degraded")
assert "database" in data
assert "redis" in data