Files
Lamont/tests/integration/conftest.py
Chelsea Lee fbdf33e894
Some checks failed
CI / test (push) Has been cancelled
CI / compose-smoke (push) Has been cancelled
Build reusable bot framework
2026-07-19 21:53:24 -05:00

175 lines
5.3 KiB
Python

"""Isolated PostgreSQL and Flask fixtures for integration tests."""
import os
from pathlib import Path
import re
import uuid
import psycopg2
from psycopg2 import sql
import pytest
from dotenv import load_dotenv
PROJECT_ROOT = Path(__file__).resolve().parents[2]
load_dotenv(PROJECT_ROOT / ".env", override=False)
TEST_DATABASE_PREFIX = "llm_bot_integration_"
SERVICE_KEY = "integration-service-key-0123456789abcdef"
ALLOWED_DISCORD_ID = "integration-discord-user"
def _databaseConfig(databaseName=None):
return {
"host": os.environ.get("DB_HOST", "localhost"),
"port": int(os.environ.get("DB_PORT", 5432)),
"dbname": databaseName or os.environ.get("DB_NAME", "app"),
"user": os.environ.get("DB_USER", "app"),
"password": os.environ.get("DB_PASS", ""),
"connect_timeout": 3,
}
def _databaseUnavailable(message):
if os.environ.get("CI", "").lower() == "true":
pytest.fail(message)
pytest.skip(message)
@pytest.fixture(scope="session")
def postgresServer():
"""Skip once locally, but fail CI when its required PostgreSQL is absent."""
sourceDatabase = os.environ.get("DB_NAME", "app")
try:
connection = psycopg2.connect(**_databaseConfig(sourceDatabase))
except psycopg2.OperationalError as error:
_databaseUnavailable(
f"PostgreSQL integration database is unavailable: {error}"
)
with connection:
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT rolsuper OR rolcreatedb AS can_create_database
FROM pg_roles
WHERE rolname = CURRENT_USER
"""
)
canCreateDatabase = cursor.fetchone()[0]
connection.close()
if not canCreateDatabase:
_databaseUnavailable(
"PostgreSQL integration user requires CREATEDB for isolated tests"
)
return sourceDatabase
@pytest.fixture
def isolatedDatabase(monkeypatch, postgresServer):
"""Create a disposable database without altering the configured database."""
databaseName = f"{TEST_DATABASE_PREFIX}{uuid.uuid4().hex}"
assert re.fullmatch(r"llm_bot_integration_[0-9a-f]{32}", databaseName)
adminConnection = psycopg2.connect(**_databaseConfig(postgresServer))
adminConnection.autocommit = True
try:
with adminConnection.cursor() as cursor:
cursor.execute(
sql.SQL("CREATE DATABASE {}").format(sql.Identifier(databaseName))
)
except psycopg2.Error as error:
adminConnection.close()
_databaseUnavailable(
f"PostgreSQL user cannot create an isolated database: {error}"
)
monkeypatch.setenv("DB_NAME", databaseName)
try:
yield databaseName
finally:
# Every production helper closes its connection, but terminate any failed-test
# leftovers before dropping only the uniquely named test database.
with adminConnection.cursor() as cursor:
cursor.execute(
"""
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = %s AND pid != pg_backend_pid()
""",
(databaseName,),
)
cursor.execute(
sql.SQL("DROP DATABASE {}").format(sql.Identifier(databaseName))
)
adminConnection.close()
@pytest.fixture
def migratedDatabase(isolatedDatabase):
from core.migrations import upgrade
applied = upgrade()
assert applied
return isolatedDatabase
@pytest.fixture
def app(migratedDatabase, monkeypatch):
monkeypatch.setenv("JWT_SECRET", "integration-only-jwt-secret")
monkeypatch.setenv("BOT_API_KEY", SERVICE_KEY)
monkeypatch.setenv(
"BOT_API_KEY_SCOPES",
"discord:session,outbox:claim,outbox:deliver",
)
monkeypatch.setenv("DISCORD_ENROLLMENT_MODE", "allowlist")
monkeypatch.setenv("DISCORD_ALLOWLIST", ALLOWED_DISCORD_ID)
monkeypatch.setenv("DEFAULT_TIMEZONE", "UTC")
from api.main import createApp
flaskApp = createApp()
flaskApp.config.update(TESTING=True)
return flaskApp
@pytest.fixture
def client(app):
return app.test_client()
@pytest.fixture
def createAuthenticatedUser(client):
created = 0
def create(username=None, password="correct-horse-battery-staple"):
nonlocal created
created += 1
username = username or f"integration-user-{created}-{uuid.uuid4().hex}"
registration = client.post(
"/api/register",
json={"username": username, "password": password, "timezone": "UTC"},
)
assert registration.status_code == 201, registration.get_json()
login = client.post(
"/api/login", json={"username": username, "password": password}
)
assert login.status_code == 200, login.get_json()
token = login.get_json()["token"]
uuidResponse = client.get(
f"/api/getUserUUID/{username}",
headers={"Authorization": f"Bearer {token}"},
)
assert uuidResponse.status_code == 200, uuidResponse.get_json()
return {
"username": username,
"password": password,
"token": token,
"user_uuid": uuidResponse.get_json(),
}
return create