Source code for tests.fixtures.general_fixtures

"""
Non specific fixtures for use across all tests.

This fixture is accessible to all tests due to its inclusion in conftest.py.

see: https://docs.pytest.org/en/6.2.x/fixture.html
"""

import pytest
from fastapi import FastAPI
from sqlalchemy.orm import Session
from starlette.testclient import TestClient

from rootski.config.config import DEPLOYMENT_ENVIRONMENT_ENV_VAR, FETCH_VALUES_FROM_SSM_ENV_VAR, Config
from rootski.main import deps
from rootski.main.deps import register_user
from rootski.main.main import create_app
from rootski.schemas.core import Services
from rootski.services.database import DBService
from rootski.services.database import models as orm

from tests.utils import scoped_env_vars
from tests.constants import CONFIG_VALUES_FOR_REAL_DATABASE, TEST_USER
from tests.mocks import MockService

####################
# --- Fixtures --- #
####################


[docs]@pytest.fixture def db_service() -> DBService: """Get a SQL Connection to a fresh database with all Rootski tables ready.""" env_vars = { # fetch actual "dev" parameters from parameter store for tests DEPLOYMENT_ENVIRONMENT_ENV_VAR: "dev", FETCH_VALUES_FROM_SSM_ENV_VAR: "true", } with scoped_env_vars(env_vars=env_vars): # connect to the test database and ping it config = Config(**CONFIG_VALUES_FOR_REAL_DATABASE) db_service = DBService.from_config(config=config) db_service.init() # clean the database orm.Base.metadata.drop_all(bind=db_service.sync_engine) orm.Base.metadata.create_all(bind=db_service.sync_engine) yield db_service # clean the database orm.Base.metadata.drop_all(bind=db_service.sync_engine)
[docs]@pytest.fixture def client(disable_auth: bool, act_as_admin: bool, db_service: DBService) -> TestClient: """ Return a TestClient with the startup event run, and where the shutdown event runs during teardown. This link explains how the "on_startup" event is triggered by this code: https://fastapi.tiangolo.com/advanced/testing-events/ :param disable_auth: if True, mocks out most of auth so that there are no dependencies on AWS Cognito :param act_as_admin: if True, the test user will be made an admin for the lifetime of the app created by this function """ app: FastAPI = make_app(disable_auth=disable_auth) # trigger "on_startup" event by instantiating TestClient with a context manager with TestClient(app) as client: # register the test user as an admin if desired if act_as_admin: db_service: DBService = app.state.services.db # tests were hanging forever when I didn't wrap the session in a context manager... why? session: Session with db_service.get_sync_session() as session: register_user(session=session, email=TEST_USER["email"], is_admin=True) yield client
############################ # --- Helper functions --- # ############################
[docs]def make_app(disable_auth=True) -> FastAPI: """Create an default instance of an app. :param enable_auth: if ``False``, disable auth so that all requests are made by the global test user. """ config = Config(**CONFIG_VALUES_FOR_REAL_DATABASE) app = create_app(config=config) if disable_auth: _disable_auth(app=app) return app
def _disable_auth(app: FastAPI): """Cause all requests to be pre-authenticated for the test user by mocking the auth service.""" # make all requests on behalf of a pre-authorized test user def fake_get_authorized_user_email(): return TEST_USER["email"] # we could override deps.get_current_user which depends on deps.get_authorized_user_email_or_anon, # but mocking only this part causes requests to actually look into the database to read # user information; it's good that our tests are involving that logic. app.dependency_overrides[deps.get_authorized_user_email_or_anon] = fake_get_authorized_user_email # replace the AuthService with a mock app_services: Services = app.state.services app_config: Config = app.state.config app_services.auth = MockService.from_config(config=app_config) return app