Files
app/backend/tests/conftest.py
T
stroblmeandClaude Opus 5 60d7ec81c0 Rename the import package app to fluksio
A wheel whose top-level module is `app` collides with anything else in a
user's venv, so the package that is about to be published takes the name
it is published under. Only the Python package moves; the repo, the
Docker WORKDIR and the compose project keep theirs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 21:48:05 +02:00

121 lines
4.0 KiB
Python

import uuid
from collections.abc import Generator
from datetime import UTC, datetime
from typing import Any
import pytest
from cryptography.hazmat.primitives.asymmetric import rsa
from fastapi.testclient import TestClient
from sqlalchemy import create_engine, text
from sqlalchemy.engine import make_url
from sqlmodel import Session, SQLModel, select
from fluksio.cloud import config as cloud_config
from fluksio.core.config import settings
from fluksio.core.db import engine, init_db
from fluksio.main import app
from fluksio.models import User
from tests.utils.portal import INSTALLATION_ID, ISSUER, jwks
from tests.utils.user import authentication_token_from_email
from tests.utils.utils import get_superuser_token_headers
@pytest.fixture(scope="session", autouse=True)
def flow_data(tmp_path_factory: pytest.TempPathFactory) -> Generator[None, None, None]:
"""Keep flows and secrets written by tests out of the real store."""
root = tmp_path_factory.mktemp("flow-data")
settings.FLOWS_DIR = root / "flows"
settings.SECRETS_FILE = root / "secrets.enc"
settings.PANELS_FILE = root / "panels.json"
yield
@pytest.fixture(scope="session", autouse=True)
def db() -> Generator[Session, None, None]:
"""Create the throwaway database `tests/__init__.py` points at, drop it after."""
url = make_url(str(settings.SQLALCHEMY_DATABASE_URI))
# The teardown drops this database, so refuse to run against anything but
# the dedicated test one.
assert url.database and url.database.endswith("_test"), url.database
maintenance = create_engine(
url.set(database="postgres"), isolation_level="AUTOCOMMIT"
)
drop = text(f'DROP DATABASE IF EXISTS "{url.database}" WITH (FORCE)')
with maintenance.connect() as connection:
connection.execute(drop)
connection.execute(text(f'CREATE DATABASE "{url.database}"'))
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
init_db(session)
yield session
engine.dispose()
with maintenance.connect() as connection:
connection.execute(drop)
maintenance.dispose()
@pytest.fixture(scope="module")
def client() -> Generator[TestClient, None, None]:
with TestClient(app) as c:
yield c
@pytest.fixture(scope="module")
def superuser_token_headers(client: TestClient) -> dict[str, str]:
return get_superuser_token_headers(client)
@pytest.fixture(scope="module")
def normal_user_token_headers(client: TestClient, db: Session) -> dict[str, str]:
return authentication_token_from_email(
client=client, email=settings.EMAIL_TEST_USER, db=db
)
@pytest.fixture
def portal_key() -> rsa.RSAPrivateKey:
return rsa.generate_private_key(public_exponent=65537, key_size=2048)
@pytest.fixture
def enrolled(
tmp_path_factory: pytest.TempPathFactory,
portal_key: rsa.RSAPrivateKey,
db: Session,
) -> Any:
"""Enrol this installation with a fake portal, then undo it."""
local_user = db.exec(
select(User).where(User.email == settings.FIRST_SUPERUSER)
).one()
original = settings.CLOUD_CONFIG_FILE
settings.CLOUD_CONFIG_FILE = (
tmp_path_factory.mktemp(f"cloud-{uuid.uuid4().hex[:6]}") / "cloud.json"
)
cloud_config.save(
cloud_config.CloudConfig(
portal_url=ISSUER,
ws_url=f"{ISSUER}/api/v1/tunnel/attach",
installation_id=INSTALLATION_ID,
token="installation-token",
issuer=ISSUER,
jwks=jwks(portal_key),
local_user_id=str(local_user.id),
enrolled_at=datetime.now(UTC).isoformat(),
portal_account=settings.FIRST_SUPERUSER,
)
)
# Enrolment also maps the enrolling account to the portal identity that
# owns the installation; without it a portal session resolves to nobody.
local_user.portal_sub = "portal-user-1"
db.add(local_user)
db.commit()
yield local_user
local_user.portal_sub = None
db.add(local_user)
db.commit()
cloud_config.delete()
settings.CLOUD_CONFIG_FILE = original