"""Sigl Test Fixtures. Simple Grocery List (Sigl) | sigl.app Copyright (c) 2022 Asymworks, LLC. All Rights Reserved. """ import os from alembic.command import upgrade from alembic.config import Config import flask import pytest from sigl.database import db as _db from sigl.factory import create_app DATA_DIR = '.pytest-data' @pytest.fixture(scope='session') def app_config(request): """Application Configuration for the Test Session. Loads configuration from config/test.py, patches the database file with a session-global temporary file, and returns the new configuration data to pass to create_app(). """ test_dir = os.path.dirname(__file__) test_cfg = os.path.join(test_dir, '../config/test.py') cfg_file = os.environ.get('SIGL_TEST_CONFIG', test_cfg) cfg_data = flask.Config(test_dir) cfg_data.from_pyfile(cfg_file) # Patch Test Configuration cfg_data['TESTING'] = True cfg_data['DB_DRIVER'] = 'sqlite' cfg_data['DB_FILE'] = os.path.join(test_dir, DATA_DIR, 'test.db') # Ensure Database Path exists db_dir = os.path.dirname(cfg_data['DB_FILE']) if not os.path.isdir(db_dir): os.mkdir(db_dir) if not os.path.isdir(db_dir): raise Exception('Database path "%s" does not exist' % (db_dir)) db_file = cfg_data['DB_FILE'] if os.path.exists(db_file): os.unlink(db_file) def teardown(): if os.path.exists(db_file): os.unlink(db_file) if os.path.exists(db_dir) and len(os.listdir(db_dir)) == 0: os.rmdir(db_dir) request.addfinalizer(teardown) return cfg_data @pytest.fixture(scope='module') def app(request, app_config): """Module-Wide Sigl Flask Application with Database. Loads configuration from config/test.py, patches the database file with a session-global temporary file, and initializes the application. """ # Apply Database Migrations _db.clear_mappers() _app = create_app(app_config, __name__) with _app.app_context(): alembic_config = Config('migrations/alembic.ini') alembic_config.set_main_option('script_location', 'migrations') upgrade(alembic_config, 'head') _db.clear_mappers() # Initialize Application and Context app = create_app(app_config, __name__) ctx = app.app_context() ctx.push() # Add Finalizers def teardown(): _db.drop_all() _db.clear_mappers() if os.path.exists(app.config['DB_FILE']): os.unlink(app.config['DB_FILE']) ctx.pop() request.addfinalizer(teardown) return app @pytest.fixture(scope='module') def app_without_database(request, app_config): """Module-Wide Sigl Flask Application without Database. Loads configuration from config/test.py and initializes the application, but does not create the database. """ _db.clear_mappers() # Initialize Application and Context app = create_app(app_config, __name__) ctx = app.app_context() ctx.push() # Add Finalizers def teardown(): _db.clear_mappers() _db.drop_all() ctx.pop() request.addfinalizer(teardown) return app @pytest.fixture(scope='function') def session(request, monkeypatch, app): """Create a new Database Session for the Request.""" with app.app_context(): connection = _db.engine.connect() transaction = connection.begin() options = dict(bind=connection, binds={}) session = _db.create_scoped_session(options=options) # Patch sigl.db with the current session monkeypatch.setattr(_db, 'session', session) def teardown(): if transaction.is_active: transaction.rollback() connection.close() session.remove() request.addfinalizer(teardown) return session