from contextlib import contextmanager import os from alembic.command import upgrade from alembic.config import Config from flask_migrate import Migrate, MigrateCommand from flask_script import Manager from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT import boto3 import psycopg2 import pytest from app import create_app, db @pytest.fixture(scope='session') def notify_api(): app = create_app() # deattach server-error error handlers - error_handler_spec looks like: # {'blueprint_name': { # status_code: [error_handlers], # None: { ExceptionClass: error_handler } # }} for error_handlers in app.error_handler_spec.values(): error_handlers.pop(500, None) if None in error_handlers: error_handlers[None] = { exc_class: error_handler for exc_class, error_handler in error_handlers[None].items() if exc_class != Exception } if error_handlers[None] == []: error_handlers.pop(None) ctx = app.app_context() ctx.push() yield app ctx.pop() @pytest.fixture(scope='function') def client(notify_api): with notify_api.test_request_context(), notify_api.test_client() as client: yield client def create_test_db(database_uri): database_name = database_uri.split('/')[-1] system_db = psycopg2.connect(dbname='postgres') system_db.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) cursor = system_db.cursor() try: cursor.execute('CREATE DATABASE {}'.format(database_name)) except psycopg2.ProgrammingError: # database "test_notification_api_x..." already exists pass finally: cursor.close() system_db.close() @pytest.fixture(scope='session') def notify_db(notify_api, worker_id): assert 'test_notification_api' in db.engine.url.database, 'dont run tests against main db' # create a database for this worker thread - from flask import current_app current_app.config['SQLALCHEMY_DATABASE_URI'] += '_{}'.format(worker_id) create_test_db(current_app.config['SQLALCHEMY_DATABASE_URI']) Migrate(notify_api, db) Manager(db, MigrateCommand) BASE_DIR = os.path.dirname(os.path.dirname(__file__)) ALEMBIC_CONFIG = os.path.join(BASE_DIR, 'migrations') config = Config(ALEMBIC_CONFIG + '/alembic.ini') config.set_main_option("script_location", ALEMBIC_CONFIG) with notify_api.app_context(): upgrade(config, 'head') yield db db.session.remove() db.get_engine(notify_api).dispose() @pytest.fixture(scope='function') def notify_db_session(notify_db): yield notify_db notify_db.session.remove() for tbl in reversed(notify_db.metadata.sorted_tables): if tbl.name not in ["provider_details", "key_types", "branding_type", "job_status", "provider_details_history", "template_process_type", "dvla_organisation", "notification_status_types", "service_permission_types"]: notify_db.engine.execute(tbl.delete()) notify_db.session.commit() @pytest.fixture def os_environ(): """ clear os.environ, and restore it after the test runs """ # for use whenever you expect code to edit environment variables old_env = os.environ.copy() class EnvironDict(dict): def __setitem__(self, key, value): assert type(value) == str super().__setitem__(key, value) os.environ = EnvironDict() yield os.environ = old_env @pytest.fixture(scope='function') def sqs_client_conn(): boto3.setup_default_session(region_name='eu-west-1') return boto3.resource('sqs') def pytest_generate_tests(metafunc): # Copied from https://gist.github.com/pfctdayelise/5719730 idparametrize = getattr(metafunc.function, 'idparametrize', None) if idparametrize: argnames, testdata = idparametrize.args ids, argvalues = zip(*sorted(testdata.items())) metafunc.parametrize(argnames, argvalues, ids=ids) @contextmanager def set_config(app, name, value): old_val = app.config.get(name) app.config[name] = value yield app.config[name] = old_val @contextmanager def set_config_values(app, dict): old_values = {} for key in dict: old_values[key] = app.config.get(key) app.config[key] = dict[key] yield for key in dict: app.config[key] = old_values[key]