Files
CIAgent/apps/api/tests/conftest.py
T
saksham 1a4c80958f Initial commit: CI Agent competitive-intelligence monitoring app
FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring,
source collection, LLM-based change analysis, enrichment, and account
security (Turnstile, escalating lockout, email verification).
2026-08-05 10:48:20 -04:00

88 lines
3.1 KiB
Python

"""Shared pytest fixtures.
Tests always run with LLM_PROVIDER=mock, SEARCH_PROVIDER=mock, a throwaway
SQLite DB, and Celery in eager mode - never against a paid provider or a
real network target.
"""
from __future__ import annotations
import os
os.environ.setdefault("APP_ENV", "test")
os.environ.setdefault("AUTH_MODE", "jwt")
os.environ.setdefault("DATABASE_URL", "sqlite+aiosqlite:///./test_ciagent.db")
os.environ.setdefault("REDIS_URL", "redis://localhost:6379/15")
os.environ.setdefault("LLM_PROVIDER", "mock")
os.environ.setdefault("SEARCH_PROVIDER", "mock")
os.environ.setdefault("CELERY_TASK_ALWAYS_EAGER", "true")
os.environ.setdefault("JWT_SECRET", "test-secret-please-change-32-characters")
# No artificial per-domain delay in tests - the collector tests hit many
# distinct mocked hostnames and shouldn't pay the real-world crawl-politeness cost.
os.environ.setdefault("SCRAPER_DOMAIN_DELAY_SECONDS", "0")
import asyncio
from pathlib import Path
import pytest
from fastapi.testclient import TestClient
from app.core.config import Settings, get_settings
from app.db.base import Base
from app.db.session import get_engine, get_sessionmaker
from app.main import app
@pytest.fixture(scope="session", autouse=True)
def _setup_database():
# The DB is a throwaway file-based SQLite DB (aiosqlite doesn't support
# ":memory:" across the multiple connections a test session opens), so it
# must be deleted up front - otherwise companies/runs left behind by a
# previous test session accumulate and pollute count-based assertions
# (e.g. the scheduler "how many companies got enqueued" tests).
db_path = Path("test_ciagent.db")
db_path.unlink(missing_ok=True)
async def _create() -> None:
engine = get_engine()
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
asyncio.run(_create())
yield
@pytest.fixture(scope="session")
def settings() -> Settings:
return get_settings()
@pytest.fixture()
def client() -> TestClient:
with TestClient(app) as c:
yield c
@pytest.fixture()
async def db_session():
"""Raw AsyncSession for tests that exercise services/repositories
directly rather than through the HTTP API."""
session_factory = get_sessionmaker()
async with session_factory() as session:
yield session
@pytest.fixture()
def local_mode_client(settings: Settings) -> TestClient:
"""A client with AUTH_MODE forced to "local" via dependency override,
independent of whatever AUTH_MODE the rest of the suite runs under - and
with its TCP peer set to loopback, since the local-dev-user bypass
(app.auth.dependencies.get_current_user) only fires for a genuinely
local request, not merely AUTH_MODE=local. Starlette's TestClient
defaults to a fake ("testclient", 50000) peer otherwise."""
local_settings = settings.model_copy(update={"auth_mode": "local"})
app.dependency_overrides[get_settings] = lambda: local_settings
with TestClient(app, client=("127.0.0.1", 51234)) as c:
yield c
app.dependency_overrides.pop(get_settings, None)