firepenguindisopanda
Refactor test documentation and improve consistency in comments
e382248
Raw
History Blame Contribute Delete
5.72 kB
"""
Pytest configuration for integration tests.
These tests hit REAL services with LIVE API keys, and only run when
RUN_LIVE_INTEGRATION=1. Without it the whole directory is skipped.
That gate is the point. Previously these ran by default against a stubbed
NVIDIA_API_KEY, every call 401'd, and each test caught its own failure with
`pytest.skip("LLM may be unavailable")` - so a suite that never once reached a
third-party service reported green. A test that cannot fail is not a test.
Run them with:
RUN_LIVE_INTEGRATION=1 pytest tests/integration -v
"""
import os
import uuid
import pytest
from fastapi.testclient import TestClient
# Ensure project root is on path
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from app.main import app
from tests.conftest import LIVE_INTEGRATION
_THIS_DIR = Path(__file__).parent
def pytest_collection_modifyitems(config, items):
"""Skip this directory unless live mode is explicitly requested.
The hook receives every collected item, not only this package's, so items
are filtered by path - marking the whole list skips the unit suite too.
"""
if LIVE_INTEGRATION:
return
skip = pytest.mark.skip(
reason="live integration; set RUN_LIVE_INTEGRATION=1 to run against real services"
)
for item in items:
if _THIS_DIR in Path(str(item.fspath)).parents:
item.add_marker(skip)
def _require_env(*vars: str) -> None:
"""Fail if a required credential is missing.
Fails rather than skips: reaching here means live mode was asked for, so an
absent credential is a misconfiguration to fix, not a reason to go quiet.
"""
missing = [v for v in vars if not os.getenv(v)]
if missing:
pytest.fail(f"Missing required env vars for live run: {', '.join(missing)}")
def require_ok(response, what: str) -> None:
"""Assert a live call succeeded, surfacing the body when it did not.
Replaces the `if failed: pytest.skip(...)` pattern. If a third-party call is
broken, a pre-deployment run must say so.
"""
assert response.status_code == 200, (
f"{what} failed with HTTP {response.status_code}: {response.text[:500]}"
)
assert "chat_error" not in response.text, (
f"{what} returned an error event: {response.text[:500]}"
)
@pytest.fixture(scope="session")
def seed_test_user():
"""Seed a test user with id=1 so FK constraints in integration tests work."""
from app.core.database import SessionLocal
from app.core.models import User
from sqlalchemy import text
db = SessionLocal()
try:
existing = db.query(User).filter_by(id=1).first()
if not existing:
user = User(
id=1,
google_id="integration_test_user",
email="integration@example.com",
full_name="Integration Test User",
persona="founder",
role="user",
)
db.add(user)
db.commit()
# Inserting an explicit id does NOT advance the Postgres sequence, so
# the next natural insert asks for id=1 and collides with this row. On a
# freshly migrated database that means the first real Google sign-up
# fails with a duplicate key error. Resync so a live-DB run leaves the
# sequence usable.
if db.bind.dialect.name == "postgresql":
db.execute(
text(
"SELECT setval(pg_get_serial_sequence('users', 'id'), "
"COALESCE((SELECT MAX(id) FROM users), 1))"
)
)
db.commit()
finally:
db.close()
@pytest.fixture(scope="session")
def auth_headers(seed_test_user):
"""Return auth headers with a valid JWT for the seeded test user."""
from datetime import datetime, timedelta, timezone
from jose import jwt
from app.core.config import settings
from app.core.database import SessionLocal
from app.core.models import User
# Use the email of whichever user has id=1 (existing or freshly seeded)
db = SessionLocal()
try:
user = db.query(User).filter(User.id == 1).first()
user_email = user.email if user else "integration@example.com"
finally:
db.close()
payload = {
"sub": user_email,
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
}
token = jwt.encode(payload, settings.secret_key, algorithm=settings.algorithm)
return {"Authorization": f"Bearer {token}"}
@pytest.fixture(scope="session")
def live_client(seed_test_user):
"""TestClient connected to the live FastAPI app (real DB, real services)."""
_require_env("NVIDIA_API_KEY", "DATABASE_URL", "SECRET_KEY")
with TestClient(app) as client:
yield client
@pytest.fixture(scope="session")
def test_session_id():
"""Generate a unique session ID for this test run."""
return str(uuid.uuid4())
@pytest.fixture(scope="session")
def test_arch_session_id():
"""Generate a unique architecture session ID for this test run."""
return f"arch_test_{uuid.uuid4().hex[:12]}"
@pytest.fixture(scope="session")
def live_db_url():
"""Return the live database URL."""
return os.getenv("DATABASE_URL", "")
@pytest.fixture(scope="session")
def pinecone_configured():
"""Return True if Pinecone is configured."""
return bool(os.getenv("PINECONE_API_KEY") and os.getenv("PINECONE_INDEX"))
@pytest.fixture(scope="session")
def redis_configured():
"""Return True if Upstash Redis is configured."""
return bool(os.getenv("UPSTASH_REDIS_REST_URL") and os.getenv("UPSTASH_REDIS_REST_TOKEN"))