56c469ddbd
Add sac_users table, JWT roles, settings/users API guarded for admin, Users page in UI, and sac_manage_user.py CLI. Co-authored-by: Cursor <cursoragent@cursor.com>
115 lines
3.1 KiB
Python
115 lines
3.1 KiB
Python
"""SQLite in-memory fixtures for API tests."""
|
|
|
|
import os
|
|
|
|
# Must be set before app.database imports create_engine
|
|
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
|
|
os.environ.setdefault("SAC_BOOTSTRAP_API_KEY", "sac_test_key_for_pytest_only")
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy import JSON, create_engine, event
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import sessionmaker
|
|
from sqlalchemy.pool import StaticPool
|
|
|
|
from app.auth.api_key import hash_api_key
|
|
from app.auth.jwt_auth import create_access_token
|
|
from app.database import Base, get_db
|
|
from app.main import app as fastapi_app
|
|
import app.models # noqa: F401 — register all tables on Base.metadata
|
|
from app.models import ApiKey, User
|
|
from app.models.user import USER_ROLE_ADMIN, USER_ROLE_MONITOR
|
|
from app.services.user_auth import hash_password
|
|
|
|
TEST_API_KEY = os.environ["SAC_BOOTSTRAP_API_KEY"]
|
|
|
|
|
|
@event.listens_for(Base.metadata, "before_create")
|
|
def _sqlite_jsonb_as_json(metadata, connection, **_kwargs) -> None:
|
|
if connection.dialect.name != "sqlite":
|
|
return
|
|
for table in metadata.tables.values():
|
|
for column in table.columns:
|
|
if isinstance(column.type, JSONB):
|
|
column.type = JSON()
|
|
|
|
|
|
@pytest.fixture
|
|
def db_engine():
|
|
engine = create_engine(
|
|
"sqlite:///:memory:",
|
|
connect_args={"check_same_thread": False},
|
|
poolclass=StaticPool,
|
|
)
|
|
Base.metadata.create_all(engine)
|
|
yield engine
|
|
engine.dispose()
|
|
|
|
|
|
@pytest.fixture
|
|
def db_session(db_engine):
|
|
Session = sessionmaker(bind=db_engine, autocommit=False, autoflush=False)
|
|
session = Session()
|
|
session.add(
|
|
ApiKey(
|
|
name="test",
|
|
key_prefix=TEST_API_KEY[:12],
|
|
key_hash=hash_api_key(TEST_API_KEY),
|
|
is_active=True,
|
|
)
|
|
)
|
|
session.add(
|
|
User(
|
|
username="test-admin",
|
|
password_hash=hash_password("test-admin-password"),
|
|
role=USER_ROLE_ADMIN,
|
|
is_active=True,
|
|
)
|
|
)
|
|
session.add(
|
|
User(
|
|
username="test-monitor",
|
|
password_hash=hash_password("test-monitor-password"),
|
|
role=USER_ROLE_MONITOR,
|
|
is_active=True,
|
|
)
|
|
)
|
|
session.commit()
|
|
yield session
|
|
session.close()
|
|
|
|
|
|
@pytest.fixture
|
|
def client(db_session, monkeypatch):
|
|
monkeypatch.setattr("app.main.bootstrap_api_key", lambda: None)
|
|
monkeypatch.setattr("app.main.bootstrap_users", lambda: None)
|
|
|
|
def override_get_db():
|
|
try:
|
|
yield db_session
|
|
finally:
|
|
pass
|
|
|
|
fastapi_app.dependency_overrides[get_db] = override_get_db
|
|
with TestClient(fastapi_app) as c:
|
|
yield c
|
|
fastapi_app.dependency_overrides.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
def auth_headers():
|
|
return {"Authorization": f"Bearer {TEST_API_KEY}"}
|
|
|
|
|
|
@pytest.fixture
|
|
def jwt_headers():
|
|
token = create_access_token("test-admin", USER_ROLE_ADMIN)
|
|
return {"Authorization": f"Bearer {token}"}
|
|
|
|
|
|
@pytest.fixture
|
|
def jwt_monitor_headers():
|
|
token = create_access_token("test-monitor", USER_ROLE_MONITOR)
|
|
return {"Authorization": f"Bearer {token}"}
|