Build reusable bot framework
Some checks failed
CI / test (push) Has been cancelled
CI / compose-smoke (push) Has been cancelled

This commit is contained in:
Chelsea Lee
2026-07-19 21:53:24 -05:00
parent 7ecc1107b2
commit fbdf33e894
66 changed files with 8428 additions and 0 deletions

View File

@@ -0,0 +1,174 @@
"""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